├── Module_29 ├── 4_TCLab │ ├── TCLabHigherOrder_4.py │ ├── TCLabHigherOrder_2.py │ ├── TCLabHigherOrder_3.py │ └── README.md ├── 2_Quiz │ └── README.md ├── 3_Assignment │ └── README.md ├── 1_Topic │ ├── README.md │ ├── ModelSimulation_1.py │ └── ModelSimulation_2.py └── README.md ├── Module_14 ├── 4_TCLab │ ├── TCLabPIControlTuning_2.py │ └── README.md ├── 2_Quiz │ └── README.md ├── 3_Assignment │ └── README.md └── README.md ├── Module_17 ├── 4_TCLab │ ├── TCLabActuator_2.py │ ├── TCLabActuator_5.py │ ├── TCLabActuator_1.py │ ├── README.md │ └── TCLabActuator_3.py ├── 2_Quiz │ └── README.md ├── 1_Topic │ └── README.md ├── 3_Assignment │ ├── README.md │ ├── ValveDesign_1.py │ └── ValveDesign_2.py └── README.md ├── Module_25 ├── 4_TCLab │ ├── TCLabBlockDiagram_2.py │ ├── TCLabBlockDiagram_5.py │ ├── README.md │ ├── TCLabBlockDiagram_4.py │ ├── TCLabBlockDiagram_1.py │ └── TCLabBlockDiagram_3.py ├── 2_Quiz │ └── README.md ├── 1_Topic │ ├── README.md │ ├── TransferFunctions_2.py │ └── TransferFunctions_1.py ├── 3_Assignment │ └── README.md └── README.md ├── Module_15 ├── 4_TCLab │ ├── TCLabPIDControlTuning_2.py │ └── README.md ├── 2_Quiz │ └── README.md ├── 3_Assignment │ └── README.md └── README.md ├── Module_18 ├── 4_TCLab │ ├── TCLabSensor_2.py │ ├── README.md │ ├── TCLabSensor_1.py │ └── TCLabSensor_3.py ├── 2_Quiz │ └── README.md ├── 3_Assignment │ └── README.md ├── 1_Topic │ └── README.md └── README.md ├── Module_06 ├── 2_Quiz │ ├── QuizFirstOrderGraphical_1.py │ ├── README.md │ └── QuizFirstOrderGraphical_2.py ├── 1_Topic │ ├── FirstOrderGraphical_1.py │ └── README.md ├── 4_TCLab │ ├── README.md │ └── TCLabFOPDT_1.py ├── 3_Assignment │ └── README.md └── README.md ├── Module_16 ├── 4_TCLab │ ├── TCLabPIDFeedforward_3.py │ ├── TCLabPIDFeedforward_5.py │ ├── README.md │ └── TCLabPIDFeedforward_1.py ├── 2_Quiz │ └── README.md ├── 3_Assignment │ └── README.md └── README.md ├── Module_31 ├── 4_TCLab │ ├── TCLabCascadeControl_4.py │ └── README.md ├── 2_Quiz │ └── README.md ├── 3_Assignment │ ├── FeedforwardCascadeControl_3.py │ ├── FeedforwardCascadeControl_5.py │ └── README.md ├── 1_Topic │ └── README.md └── README.md ├── Module_02 ├── 3_Assignment │ ├── SimulateCOVID19_2.py │ ├── SimulateCOVID19_1.py │ ├── README.md │ ├── SimulateCOVID19_3.py │ ├── SimulateCOVID19_4.py │ └── SimulateCOVID19_5.py ├── 2_Quiz │ ├── QuizSolveODEs_1.py │ ├── README.md │ └── QuizSolveODEs_2.py ├── 4_TCLab │ ├── README.md │ ├── TCLabSim_1.py │ ├── TCLabSim_2.py │ └── TCLabSim_3.py ├── 1_Topic │ ├── README.md │ ├── SolveDifferentialEquations_3.py │ ├── SolveDifferentialEquations_1.py │ ├── SolveDifferentialEquations_5.py │ ├── SolveDifferentialEquations_4.py │ ├── SolveDifferentialEquations_2.py │ └── SolveDifferentialEquations_6.py └── README.md ├── Module_12 ├── 2_Quiz │ ├── QuizProportionalIntegralControl_1.py │ └── README.md ├── 3_Assignment │ └── README.md ├── 4_TCLab │ ├── README.md │ ├── TCLabPIControl_1.py │ └── TCLabPIControl_2.py ├── 1_Topic │ └── README.md └── README.md ├── Module_01 ├── 3_Assignment │ ├── InstallPython_1.py │ └── README.md ├── 1_Topic │ └── README.md └── README.md ├── Module_37 ├── 2_Quiz │ ├── README.md │ └── QuizSchedulingControl_1.py ├── 1_Topic │ └── README.md └── README.md ├── Module_26 ├── 2_Quiz │ ├── README.md │ └── QuizStateSpace_1.py ├── 3_Assignment │ ├── ReactorStateSpace_1.py │ └── README.md ├── 4_TCLab │ ├── README.md │ ├── TCLabStateSpace_4.py │ ├── TCLabStateSpace_3.py │ └── TCLabStateSpace_2.py ├── 1_Topic │ ├── README.md │ └── StateSpaceModel_1.py └── README.md ├── Module_30 ├── 2_Quiz │ └── README.md ├── 1_Topic │ └── README.md ├── 3_Assignment │ ├── README.md │ ├── StabilityAnalysisExercises_1.py │ ├── StabilityAnalysisExercises_4.py │ └── StabilityAnalysisExercises_3.py ├── 4_TCLab │ └── README.md └── README.md ├── Module_03 ├── 1_Topic │ ├── README.md │ └── DynamicModeling_1.py ├── 4_TCLab │ ├── README.md │ ├── TCLabConvection_1.py │ ├── TCLabConvection_2.py │ └── TCLabConvection_3.py ├── 3_Assignment │ └── README.md ├── 2_Quiz │ └── README.md └── README.md ├── Module_39 ├── 2_Quiz │ └── README.md └── README.md ├── Module_04 ├── 4_TCLab │ ├── README.md │ ├── TCLabRadiative_1.py │ ├── TCLabRadiative_2.py │ └── TCLabRadiative_3.py ├── 3_Assignment │ ├── README.md │ └── TankBlending_1.py ├── 1_Topic │ └── README.md ├── 2_Quiz │ └── README.md └── README.md ├── Module_05 ├── 4_TCLab │ ├── README.md │ ├── TCLabLinearize_3.py │ ├── TCLabLinearize_1.py │ └── TCLabLinearize_2.py ├── 2_Quiz │ ├── README.md │ └── QuizLinearization_1.py ├── 3_Assignment │ ├── LinearizeDiffEqns_1.py │ ├── README.md │ ├── LinearizeDiffEqns_2.py │ ├── LinearizeDiffEqns_3.py │ └── LinearizeDiffEqns_4.py ├── 1_Topic │ ├── README.md │ ├── ModelLinearization_1.py │ ├── ModelLinearization_2.py │ └── ModelLinearization_3.py └── README.md ├── Module_36 ├── 2_Quiz │ ├── README.md │ ├── QuizLinearProgramming_2.py │ └── QuizLinearProgramming_1.py ├── 1_Topic │ ├── README.md │ ├── LinearProgramming_3.py │ ├── LinearProgramming_4.py │ ├── LinearProgramming_1.py │ ├── LinearProgramming_2.py │ ├── LinearProgramming_5.py │ └── LinearProgramming_6.py └── README.md ├── Module_13 ├── 2_Quiz │ └── README.md ├── 4_TCLab │ ├── README.md │ └── TCLabPIDControl_1.py ├── 3_Assignment │ ├── README.md │ └── ControlBlending_1.py ├── 1_Topic │ ├── README.md │ └── ProportionalIntegralDerivative_1.py └── README.md ├── Module_23 ├── 1_Topic │ ├── README.md │ └── ArduinoControl_1.py ├── 4_TCLab │ ├── README.md │ └── ArduinoControl2_2.py └── README.md ├── Module_24 ├── 2_Quiz │ └── README.md ├── 4_TCLab │ ├── README.md │ ├── TCLabImpulseResponse_1.py │ └── TCLabImpulseResponse_2.py ├── 3_Assignment │ ├── README.md │ ├── LaplaceApplications_2.py │ └── LaplaceApplications_1.py ├── 1_Topic │ ├── README.md │ └── LaplaceTransforms_1.py └── README.md ├── Module_35 ├── 2_Quiz │ └── README.md ├── 1_Topic │ └── README.md └── README.md ├── Module_07 ├── 4_TCLab │ ├── README.md │ └── TCLabRegression_1.py ├── 3_Assignment │ ├── README.md │ └── DynamicParameterRegression_1.py ├── 2_Quiz │ └── README.md ├── 1_Topic │ ├── README.md │ └── FirstOrderOptimization_1.py └── README.md ├── Module_21 ├── 1_Topic │ ├── README.md │ ├── ArduinoModeling_2.py │ ├── ArduinoModeling_3.py │ ├── ArduinoModeling_1.py │ └── ArduinoModeling_4.py ├── 4_TCLab │ ├── README.md │ ├── ArduinoModeling2_3.py │ └── ArduinoModeling2_2.py └── README.md ├── Module_27 ├── 2_Quiz │ └── README.md ├── 3_Assignment │ ├── README.md │ └── SecondOrderApplications_1.py ├── 4_TCLab │ ├── README.md │ ├── TCLabOnOffControl_2.py │ ├── TCLabOnOffControl_1.py │ └── TCLabOnOffControl_3.py ├── 1_Topic │ ├── README.md │ └── SecondOrderSystems_1.py └── README.md ├── Module_28 ├── 2_Quiz │ ├── README.md │ ├── QuizSecondOrderOptimization_2.py │ ├── QuizSecondOrderOptimization_4.py │ └── QuizSecondOrderOptimization_1.py ├── 4_TCLab │ ├── README.md │ ├── TCLabSecondOrder_1.py │ └── TCLabSecondOrder_4.py ├── 3_Assignment │ ├── README.md │ ├── SecondOrderOptimizationFit_1.py │ └── SecondOrderOptimizationFit_2.py ├── 1_Topic │ ├── README.md │ └── SecondOrderOptimization_1.py └── README.md ├── Module_40 ├── 1_Topic │ └── README.md ├── 2_Quiz │ └── README.md └── README.md ├── Module_22 ├── 1_Topic │ └── README.md ├── 4_TCLab │ ├── README.md │ ├── ArduinoEstimation2_9.py │ ├── ArduinoEstimation2_1.py │ └── ArduinoEstimation2_8.py └── README.md ├── Module_38 ├── 2_Quiz │ ├── README.md │ ├── QuizNonlinearProgramming_3.py │ ├── QuizNonlinearProgramming_4.py │ ├── QuizNonlinearProgramming_1.py │ └── QuizNonlinearProgramming_2.py ├── 1_Topic │ ├── README.md │ ├── NonlinearProgramming_2.py │ └── NonlinearProgramming_1.py └── README.md ├── Module_10 ├── 1_Topic │ └── README.md ├── 3_Assignment │ └── README.md ├── 4_TCLab │ └── README.md ├── 2_Quiz │ └── README.md └── README.md ├── Module_11 ├── 2_Quiz │ └── README.md ├── 1_Topic │ └── README.md ├── 4_TCLab │ ├── README.md │ ├── TCLabPOnlyControl_2.py │ └── TCLabPOnlyControl_3.py ├── 3_Assignment │ └── README.md └── README.md └── README.md /Module_29/4_TCLab/TCLabHigherOrder_4.py: -------------------------------------------------------------------------------- 1 | print(sys.to_tf()) -------------------------------------------------------------------------------- /Module_14/4_TCLab/TCLabPIControlTuning_2.py: -------------------------------------------------------------------------------- 1 | Kc = 5 2 | tauI = 120 -------------------------------------------------------------------------------- /Module_17/4_TCLab/TCLabActuator_2.py: -------------------------------------------------------------------------------- 1 | Kp2 = 2 | taup2 = 3 | thetap2 = -------------------------------------------------------------------------------- /Module_25/4_TCLab/TCLabBlockDiagram_2.py: -------------------------------------------------------------------------------- 1 | T1Pred = # fill in analytic solution -------------------------------------------------------------------------------- /Module_29/4_TCLab/TCLabHigherOrder_2.py: -------------------------------------------------------------------------------- 1 | sys = signal.StateSpace(A,B,C,D) 2 | t,y,x = signal.lsim(sys,u,t) -------------------------------------------------------------------------------- /Module_17/4_TCLab/TCLabActuator_5.py: -------------------------------------------------------------------------------- 1 | Kp2 = 0.45 2 | taup2 = 175.0 3 | thetap2 = 15.0 4 | Tss = 23.0 5 | Qstep = 65 -------------------------------------------------------------------------------- /Module_25/4_TCLab/TCLabBlockDiagram_5.py: -------------------------------------------------------------------------------- 1 | T1Pred = 20 + 6.14*np.exp(-0.054*tm) - 26.13*np.exp(-0.012*tm) + 23.0 -------------------------------------------------------------------------------- /Module_15/4_TCLab/TCLabPIDControlTuning_2.py: -------------------------------------------------------------------------------- 1 | # PID Parameters 2 | Kc = 5.0 3 | tauI = 120.0 # sec 4 | tauD = 2.0 # sec -------------------------------------------------------------------------------- /Module_18/4_TCLab/TCLabSensor_2.py: -------------------------------------------------------------------------------- 1 | import time 2 | import tclab 3 | lab = tclab.TCLab() 4 | print(lab.T1) 5 | lab.close() -------------------------------------------------------------------------------- /Module_06/2_Quiz/QuizFirstOrderGraphical_1.py: -------------------------------------------------------------------------------- 1 | # calculate model with updated parameters 2 | Km = 2.0 3 | taum = 2.0 4 | thetam = 2.0 -------------------------------------------------------------------------------- /Module_16/4_TCLab/TCLabPIDFeedforward_3.py: -------------------------------------------------------------------------------- 1 | # PID Parameters 2 | Kc = 5.0 3 | tauI = 120.0 # sec 4 | tauD = 2.0 # sec 5 | Kff = 0.0 -------------------------------------------------------------------------------- /Module_16/4_TCLab/TCLabPIDFeedforward_5.py: -------------------------------------------------------------------------------- 1 | # PID Parameters 2 | Kc = 10.0 3 | tauI = 50.0 # sec 4 | tauD = 2.0 # sec 5 | Kff = -0.38 -------------------------------------------------------------------------------- /Module_31/4_TCLab/TCLabCascadeControl_4.py: -------------------------------------------------------------------------------- 1 | # PID Parameters 2 | Kc1 = 6.5 3 | tauI1 = 85.0 # sec 4 | Kc2 = 7.5 5 | tauI2 = 110.0 # sec -------------------------------------------------------------------------------- /Module_29/4_TCLab/TCLabHigherOrder_3.py: -------------------------------------------------------------------------------- 1 | ss = GEKKO() 2 | x,y,u = ss.state_space(A,B,C,D=None) 3 | ss.options.IMODE = 7 4 | ss.solve(disp=False) -------------------------------------------------------------------------------- /Module_06/1_Topic/FirstOrderGraphical_1.py: -------------------------------------------------------------------------------- 1 | # calculate model with updated parameters 2 | Km = 2.5 3 | taum = 3.0 4 | thetam = 5.0 5 | ym = sim_model(Km,taum,thetam) -------------------------------------------------------------------------------- /Module_02/3_Assignment/SimulateCOVID19_2.py: -------------------------------------------------------------------------------- 1 | dx[0] = # dS/dt equation 2 | dx[1] = # dE/dt equation 3 | dx[2] = # dI/dt equation 4 | dx[3] = # dR/dt equation -------------------------------------------------------------------------------- /Module_12/2_Quiz/QuizProportionalIntegralControl_1.py: -------------------------------------------------------------------------------- 1 | if op[i] < op_lo: # check lower limit 2 | op[i] = op_lo 3 | ie[i] = ie[i] - e[i] * delta_t # anti-reset windup -------------------------------------------------------------------------------- /Module_17/4_TCLab/TCLabActuator_1.py: -------------------------------------------------------------------------------- 1 | import time 2 | import tclab 3 | lab = tclab.TCLab() 4 | lab.Q1(65) 5 | time.sleep(10) 6 | lab.Q2(85) 7 | time.sleep(5) 8 | lab.close() -------------------------------------------------------------------------------- /Module_02/2_Quiz/QuizSolveODEs_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.integrate import odeint 3 | def model(y,t): 4 | k = 0.3 5 | return -k * y 6 | y = odeint(model,5,[0,3]) -------------------------------------------------------------------------------- /Module_01/3_Assignment/InstallPython_1.py: -------------------------------------------------------------------------------- 1 | module='gekko' 2 | try: 3 | from pip import main as pipmain 4 | except: 5 | from pip._internal import main as pipmain 6 | pipmain(['install',module]) -------------------------------------------------------------------------------- /Module_37/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Optimization](https://www.apmonitor.com/pdc/index.php/Main/QuizSchedulingControl) 2 | - Scripts Located: 1 3 | - Page Description: Learning assessment on optimization. 4 | -------------------------------------------------------------------------------- /Module_26/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on State Space Models](https://www.apmonitor.com/pdc/index.php/Main/QuizStateSpace) 2 | - Scripts Located: 1 3 | - Page Description: Learning assessment on state space models. 4 | -------------------------------------------------------------------------------- /Module_26/3_Assignment/ReactorStateSpace_1.py: -------------------------------------------------------------------------------- 1 | # Linearized equation non-zero elements 2 | A[0,0] = ### fill-in value ### 3 | B[0,0] = ### fill-in value ### 4 | A[1,0] = ### fill-in value ### 5 | A[1,1] = ### fill-in value ### -------------------------------------------------------------------------------- /Module_30/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Stability Analysis](https://www.apmonitor.com/pdc/index.php/Main/QuizStabilityAnalysis) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on stability analysis. 4 | -------------------------------------------------------------------------------- /Module_03/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Dynamic Model Introduction](https://www.apmonitor.com/pdc/index.php/Main/DynamicModeling) 2 | - Scripts Located: 1 3 | - Page Description: Introduction to dynamic modeling and a course overview 4 | -------------------------------------------------------------------------------- /Module_18/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Sensors and Data Acquisition](https://www.apmonitor.com/pdc/index.php/Main/QuizSensors) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on sensors and data acquisition. 4 | -------------------------------------------------------------------------------- /Module_03/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab Convective Heat Transfer](https://www.apmonitor.com/pdc/index.php/Main/TCLabConvection) 2 | - Scripts Located: 3 3 | - Page Description: Convective Heat Transfer Prediction with a Transistor Heater 4 | -------------------------------------------------------------------------------- /Module_17/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Flow Valves](https://www.apmonitor.com/pdc/index.php/Main/QuizFlowValves) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on flow valve design and flow control PID tuning. 4 | -------------------------------------------------------------------------------- /Module_02/3_Assignment/SimulateCOVID19_1.py: -------------------------------------------------------------------------------- 1 | u = 0.1 # social distancing (0-1) 2 | # 0 = no social distancing 3 | # 0.1 = masks 4 | # 0.2 = masks and hybrid classes 5 | # 0.3 = masks, hybrid, and online classes -------------------------------------------------------------------------------- /Module_14/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Level Control Case Study](https://www.apmonitor.com/pdc/index.php/Main/QuizLevelControl) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on level control with a PID controller. 4 | -------------------------------------------------------------------------------- /Module_02/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab Simulate Step Response](https://www.apmonitor.com/pdc/index.php/Main/TCLabSim) 2 | - Scripts Located: 3 3 | - Page Description: Dynamic Temperature Response of a Heater and Temperature Sensor with an Arduino 4 | -------------------------------------------------------------------------------- /Module_18/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab Temperature Sensor](https://www.apmonitor.com/pdc/index.php/Main/TCLabSensor) 2 | - Scripts Located: 3 3 | - Page Description: Dynamic Temperature Response of a Heater and Temperature Sensor with an Arduino 4 | -------------------------------------------------------------------------------- /Module_29/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Higher Order Simulation](https://www.apmonitor.com/pdc/index.php/Main/QuizModelSimulation) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on higher order dynamic system simulation. 4 | -------------------------------------------------------------------------------- /Module_31/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Feedforward and Cascade Control](https://www.apmonitor.com/pdc/index.php/Main/QuizFeedforwardCascade) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on feedforward and cascade control. 4 | -------------------------------------------------------------------------------- /Module_39/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Machine Learning Classification](https://www.apmonitor.com/pdc/index.php/Main/QuizMachineLearning) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on machine learning classification. 4 | -------------------------------------------------------------------------------- /Module_04/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab Radiative Heat Transfer](https://www.apmonitor.com/pdc/index.php/Main/TCLabRadiative) 2 | - Scripts Located: 3 3 | - Page Description: Radiative and Convective Heat Transfer Prediction with a Transistor Heater 4 | -------------------------------------------------------------------------------- /Module_05/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab Linearize Energy Balance](https://www.apmonitor.com/pdc/index.php/Main/TCLabLinearize) 2 | - Scripts Located: 3 3 | - Page Description: Radiative and Convective Heat Transfer Prediction with a Transistor Heater 4 | -------------------------------------------------------------------------------- /Module_06/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab FOPDT Model](https://www.apmonitor.com/pdc/index.php/Main/TCLabFOPDT) 2 | - Scripts Located: 1 3 | - Page Description: Linear Dynamic Temperature Response Model of a Heater and Temperature Sensor with an Arduino 4 | -------------------------------------------------------------------------------- /Module_12/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Automobile Velocity Control](https://www.apmonitor.com/pdc/index.php/Main/SpeedControl) 2 | - Scripts Located: 1 3 | - Page Description: Case study on automobile velocity control with a PI controller in Python 4 | -------------------------------------------------------------------------------- /Module_36/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Linear Programming](https://www.apmonitor.com/pdc/index.php/Main/QuizLinearProgramming) 2 | - Scripts Located: 2 3 | - Page Description: Learning assessment on linear programming with simple tutorial problems. 4 | -------------------------------------------------------------------------------- /Module_13/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on PID Control](https://www.apmonitor.com/pdc/index.php/Main/QuizProportionalIntegralDerivative) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on Proportional Integral Derivative (PID) Control. 4 | -------------------------------------------------------------------------------- /Module_23/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Arduino Temperature PID Control](https://www.apmonitor.com/pdc/index.php/Main/ArduinoControl) 2 | - Scripts Located: 2 3 | - Page Description: Implement Temperature Control and Test Set Point Changes with an Arduino Device 4 | -------------------------------------------------------------------------------- /Module_24/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Laplace Transforms](https://www.apmonitor.com/pdc/index.php/Main/QuizLaplaceTransforms) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on Laplace transforms with Final and Initial Value Theorem. 4 | -------------------------------------------------------------------------------- /Module_26/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab State Space Model](https://www.apmonitor.com/pdc/index.php/Main/TCLabStateSpace) 2 | - Scripts Located: 5 3 | - Page Description: Derive and simulate a state space model of temperature response to heater step changes 4 | -------------------------------------------------------------------------------- /Module_35/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Multi-Scale Simulation and Optimization](https://www.apmonitor.com/pdc/index.php/Main/QuizOptimization) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on simulation and multi-scale optimization. 4 | -------------------------------------------------------------------------------- /Module_02/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz: Solve ODEs with Python](https://www.apmonitor.com/pdc/index.php/Main/QuizSolveODEs) 2 | - Scripts Located: 2 3 | - Page Description: Learning assessment on solving differential equations with numerical methods in Python. 4 | -------------------------------------------------------------------------------- /Module_07/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab FOPDT Regression](https://www.apmonitor.com/pdc/index.php/Main/TCLabRegression) 2 | - Scripts Located: 2 3 | - Page Description: Linear Dynamic Temperature Response Model of a Heater and Temperature Sensor with an Arduino 4 | -------------------------------------------------------------------------------- /Module_15/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on CSTR Case Study](https://www.apmonitor.com/pdc/index.php/Main/QuizReactorControl) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on temperature control of a continuously stirred tank reactor (CSTR). 4 | -------------------------------------------------------------------------------- /Module_21/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Arduino Dynamic Temperature Modeling](https://www.apmonitor.com/pdc/index.php/Main/ArduinoModeling) 2 | - Scripts Located: 5 3 | - Page Description: Dynamic Temperature Response of a Heater and Temperature Sensor with an Arduino 4 | -------------------------------------------------------------------------------- /Module_21/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [Arduino Dynamic Response: 2 Heaters](https://www.apmonitor.com/pdc/index.php/Main/ArduinoModeling2) 2 | - Scripts Located: 5 3 | - Page Description: Dynamic Temperature Response of a Heater and Temperature Sensor with an Arduino 4 | -------------------------------------------------------------------------------- /Module_27/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Second Order Graphical Methods](https://www.apmonitor.com/pdc/index.php/Main/QuizSecondOrderSystems) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on second order dynamic system graphical methods. 4 | -------------------------------------------------------------------------------- /Module_28/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Second Order Regression](https://www.apmonitor.com/pdc/index.php/Main/QuizSecondOrderOptimization) 2 | - Scripts Located: 4 3 | - Page Description: Learning assessment on second order dynamic system regression methods. 4 | -------------------------------------------------------------------------------- /Module_40/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Model Predictive Control](https://www.apmonitor.com/pdc/index.php/Main/ModelPredictiveControl) 2 | - Scripts Located: 1 3 | - Page Description: Dynamic control in MATLAB and Python for use in real-time or off-line applications 4 | -------------------------------------------------------------------------------- /Module_03/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Derive Transient Balance Equations](https://www.apmonitor.com/pdc/index.php/Main/DeriveBalanceEquations) 2 | - Scripts Located: 0 3 | - Page Description: The exercise involves deriving a transient model from balance equations. 4 | -------------------------------------------------------------------------------- /Module_22/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Energy Balance Parameter Estimation](https://www.apmonitor.com/pdc/index.php/Main/ArduinoEstimation) 2 | - Scripts Located: 2 3 | - Page Description: Parameter Estimation of Temperature Response of Heater and Thermistor on an Arduino 4 | -------------------------------------------------------------------------------- /Module_23/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [Interacting PID Control](https://www.apmonitor.com/pdc/index.php/Main/ArduinoControl2) 2 | - Scripts Located: 3 3 | - Page Description: Implement Temperature PID Control with Feedforward and Test Set Point Changes with Arduino Data 4 | -------------------------------------------------------------------------------- /Module_26/4_TCLab/TCLabStateSpace_4.py: -------------------------------------------------------------------------------- 1 | # state space simulation with scipy 2 | sys = signal.StateSpace(Am,Bm,Cm,Dm) 3 | tsys = data['Time'].values 4 | Qsys = np.vstack((data['Q1'].values,data['Q2'].values)) 5 | Qsys = Qsys.T 6 | tsys,ysys,xsys = signal.lsim(sys,Qsys,tsys) -------------------------------------------------------------------------------- /Module_01/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Process Dynamics and Control in Python](https://www.apmonitor.com/pdc/index.php/Main/HomePage) 2 | - Scripts Located: 0 3 | - Page Description: Python Process Control and Dynamics Course in Chemical Engineering at Brigham Young University 4 | -------------------------------------------------------------------------------- /Module_12/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab PI Control](https://www.apmonitor.com/pdc/index.php/Main/TCLabPIControl) 2 | - Scripts Located: 2 3 | - Page Description: TCLab with proportional integral (PI) control eliminates offset between the setpoint and measured temperature 4 | -------------------------------------------------------------------------------- /Module_13/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab PID Control](https://www.apmonitor.com/pdc/index.php/Main/TCLabPIDControl) 2 | - Scripts Located: 3 3 | - Page Description: TCLab with proportional integral derivative (PID) control. Use IMC and ITAE tuning and compare the response 4 | -------------------------------------------------------------------------------- /Module_16/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Artificial Pancreas Case Study](https://www.apmonitor.com/pdc/index.php/Main/QuizDiabetesControl) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on case study of an artificial pancreas control development. 4 | -------------------------------------------------------------------------------- /Module_24/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab Impulse Response](https://www.apmonitor.com/pdc/index.php/Main/TCLabImpulseResponse) 2 | - Scripts Located: 2 3 | - Page Description: Laplace Transform solution of a Heater impulse and Temperature dynamic response with an Arduino 4 | -------------------------------------------------------------------------------- /Module_26/4_TCLab/TCLabStateSpace_3.py: -------------------------------------------------------------------------------- 1 | # state space simulation 2 | ss = GEKKO() 3 | x,y,u = ss.state_space(Am,Bm,Cm,D=None) 4 | u[0].value = data['Q1'].values 5 | u[1].value = data['Q2'].values 6 | ss.time = data['Time'].values 7 | ss.options.IMODE = 7 8 | ss.solve(disp=False) -------------------------------------------------------------------------------- /Module_01/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Install Python](https://www.apmonitor.com/pdc/index.php/Main/InstallPython) 2 | - Scripts Located: 1 3 | - Page Description: Python is a general-purpose programming language. It is used in this course for process dynamics and control. 4 | -------------------------------------------------------------------------------- /Module_04/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Tank Blending](https://www.apmonitor.com/pdc/index.php/Main/TankBlending) 2 | - Scripts Located: 1 3 | - Page Description: Case study on modeling of a tank mixer. The exercise involves creating a dynamic model and simulating it in Python. 4 | -------------------------------------------------------------------------------- /Module_05/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz: Linearize ODEs](https://www.apmonitor.com/pdc/index.php/Main/QuizLinearization) 2 | - Scripts Located: 1 3 | - Page Description: Learning assessment on linearizing differential equations and transforming the equation into deviation form. 4 | -------------------------------------------------------------------------------- /Module_28/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab Second Order Regression](https://www.apmonitor.com/pdc/index.php/Main/TCLabSecondOrder) 2 | - Scripts Located: 4 3 | - Page Description: A 2nd order model is fit to an open-loop and closed-loop temperature response to heater changes. 4 | -------------------------------------------------------------------------------- /Module_38/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Nonlinear Programming](https://www.apmonitor.com/pdc/index.php/Main/QuizNonlinearProgramming) 2 | - Scripts Located: 4 3 | - Page Description: Learning assessment on nonlinear programming with the KKT conditions and example problems. 4 | -------------------------------------------------------------------------------- /Module_40/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Model Predictive Control](https://www.apmonitor.com/pdc/index.php/Main/QuizModelPredictiveControl) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on model predictive control and when MPC is needed over PID control. 4 | -------------------------------------------------------------------------------- /Module_10/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Process Control Introduction](https://www.apmonitor.com/pdc/index.php/Main/FeedbackControl) 2 | - Scripts Located: 0 3 | - Page Description: Introduction to process controllers with examples of actuators, sensors, and controllers for engineers. 4 | -------------------------------------------------------------------------------- /Module_28/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Fit Second Order with Optimization](https://www.apmonitor.com/pdc/index.php/Main/SecondOrderOptimizationFit) 2 | - Scripts Located: 2 3 | - Page Description: Optimization methods are used to fit unknown parameters for a second order system. 4 | -------------------------------------------------------------------------------- /Module_31/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab Cascade Control](https://www.apmonitor.com/pdc/index.php/Main/TCLabCascadeControl) 2 | - Scripts Located: 4 3 | - Page Description: Tune a cascade controller for the Temperature Control Lab by selecting controller gains and time constants. 4 | -------------------------------------------------------------------------------- /Module_02/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Solve Differential Equations with ODEINT](https://www.apmonitor.com/pdc/index.php/Main/SolveDifferentialEquations) 2 | - Scripts Located: 6 3 | - Page Description: A beginning tutorial on solving differential equations with numerical methods in Python. 4 | -------------------------------------------------------------------------------- /Module_22/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [Parameter Regression with Arduino Data](https://www.apmonitor.com/pdc/index.php/Main/ArduinoEstimation2) 2 | - Scripts Located: 10 3 | - Page Description: Parameter Estimation of Temperature Response of Two Heaters and Two Thermistors with an Arduino 4 | -------------------------------------------------------------------------------- /Module_10/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Process Controller Design Exercises](https://www.apmonitor.com/pdc/index.php/Main/ControllerDesign) 2 | - Scripts Located: 0 3 | - Page Description: Introduction to process controllers with examples of actuators, sensors, and controllers for engineers. 4 | -------------------------------------------------------------------------------- /Module_16/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab PID with Feedforward](https://www.apmonitor.com/pdc/index.php/Main/TCLabPIDFeedforward) 2 | - Scripts Located: 5 3 | - Page Description: TCLab with proportional integral derivative (PID) control tuning and feedforward trim for disturbance rejection. 4 | -------------------------------------------------------------------------------- /Module_29/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab Higher Order Models](https://www.apmonitor.com/pdc/index.php/Main/TCLabHigherOrder) 2 | - Scripts Located: 5 3 | - Page Description: Derive and simulate a transfer function, state space, and time series model of T2 temperature response to heater Q1 4 | -------------------------------------------------------------------------------- /Module_38/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Nonlinear Programming with Python](https://www.apmonitor.com/pdc/index.php/Main/NonlinearProgramming) 2 | - Scripts Located: 2 3 | - Page Description: Optimization with Python - Problem-Solving Techniques for Chemical Engineers at Brigham Young University 4 | -------------------------------------------------------------------------------- /Module_06/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz: FOPDT Graphical Fit](https://www.apmonitor.com/pdc/index.php/Main/QuizFirstOrderGraphical) 2 | - Scripts Located: 2 3 | - Page Description: Learning assessment on obtaining parameters for a first order linear differential equation with a graphical method. 4 | -------------------------------------------------------------------------------- /Module_11/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Proportional Control](https://www.apmonitor.com/pdc/index.php/Main/QuizProportionalControl) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on proportional control as the most basic type of Proportional Integral Derivative (PID) Control. 4 | -------------------------------------------------------------------------------- /Module_04/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Balance Equations](https://www.apmonitor.com/pdc/index.php/Main/PhysicsBasedModels) 2 | - Scripts Located: 1 3 | - Page Description: Balance equations are the foundation of many physics-based models. Mass, energy, momentum, and species balances are from conservation laws. 4 | -------------------------------------------------------------------------------- /Module_17/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab Heater Actuators](https://www.apmonitor.com/pdc/index.php/Main/TCLabActuator) 2 | - Scripts Located: 5 3 | - Page Description: The Temperature Control Lab (TCLab) uses TIP31C transistor as heaters to actuate the power dissipation and thereby control the temperature 4 | -------------------------------------------------------------------------------- /Module_10/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab Controller Design](https://www.apmonitor.com/pdc/index.php/Main/TCLabControlDesign) 2 | - Scripts Located: 0 3 | - Page Description: Design a controller for automation of temperature regulation to a setpoint. The controller adjusts a heater to regulate the temperature. 4 | -------------------------------------------------------------------------------- /Module_13/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Concentration PID Control](https://www.apmonitor.com/pdc/index.php/Main/ControlBlending) 2 | - Scripts Located: 1 3 | - Page Description: Case study on controlling tank mixer concentration. The exercise involves using a dynamic model and adding a PI controller in Python. 4 | -------------------------------------------------------------------------------- /Module_03/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz: Physics-based Modeling](https://www.apmonitor.com/pdc/index.php/Main/QuizBalanceEquations) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on physics-based modeling methods with an introduction to conserved quantities such as mass, energy, and momentum. 4 | -------------------------------------------------------------------------------- /Module_25/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Transfer Functions](https://www.apmonitor.com/pdc/index.php/Main/QuizTransferFunctions) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on transfer function analysis including how to calculate gain, compute input to output relationships, and limitations. 4 | -------------------------------------------------------------------------------- /Module_25/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab Block Diagram Analysis](https://www.apmonitor.com/pdc/index.php/Main/TCLabBlockDiagram) 2 | - Scripts Located: 5 3 | - Page Description: Design a controller for automation of temperature regulation to a setpoint. The controller adjusts a heater to regulate the temperature. 4 | -------------------------------------------------------------------------------- /Module_28/2_Quiz/QuizSecondOrderOptimization_2.py: -------------------------------------------------------------------------------- 1 | # define objective 2 | def objective(x): 3 | # simulate model 4 | ym = sim_model(x) 5 | # calculate objective 6 | obj = 0.0 7 | for i in range(len(ym)): 8 | obj = obj + (ym[i]-yp[i])**2 9 | # return result 10 | return obj -------------------------------------------------------------------------------- /Module_28/2_Quiz/QuizSecondOrderOptimization_4.py: -------------------------------------------------------------------------------- 1 | # define objective 2 | def objective(x): 3 | # simulate model 4 | ym = sim_model(x) 5 | # calculate objective 6 | obj = 0.0 7 | for i in range(len(ym)): 8 | obj = obj + (ym[i]-yp[i])**2 9 | # return result 10 | return obj -------------------------------------------------------------------------------- /Module_31/3_Assignment/FeedforwardCascadeControl_3.py: -------------------------------------------------------------------------------- 1 | #### Highlighting the parts that need to be changed #### 2 | # Controller tuning 3 | Kff = -3.0 4 | 5 | # simulate with ODEINT 6 | for i in range(n-1): 7 | ##### add feedforward to controller 8 | valve = ubias + P[i] + I[i] + Kff * (Fin[i]-Fin[0]) 9 | ##### -------------------------------------------------------------------------------- /Module_37/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Refinery Optimization](https://www.apmonitor.com/pdc/index.php/Main/RefineryOptimization) 2 | - Scripts Located: 0 3 | - Page Description: Refineries are optimized with either linear or nonlinear programming solvers to maximize revenue and meet customer contracts for production. 4 | -------------------------------------------------------------------------------- /Module_29/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Distillate Composition Control](https://www.apmonitor.com/pdc/index.php/Main/DistillationControl) 2 | - Scripts Located: 3 3 | - Page Description: Maintain an overhead mole fraction for the light component in a binary distillation column that separates cyclohexane and n-heptane. 4 | -------------------------------------------------------------------------------- /Module_31/3_Assignment/FeedforwardCascadeControl_5.py: -------------------------------------------------------------------------------- 1 | #### Highlighting the parts that need to be changed #### 2 | # Controller tuning 3 | Kff = 3.333 4 | 5 | # simulate with ODEINT 6 | for i in range(n-1): 7 | ##### add feedforward to controller 8 | valve = ubias + P[i] + I[i] + Kff * (Fout[i]-Fout[0]) 9 | ##### -------------------------------------------------------------------------------- /Module_04/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz: Transient Balance Equations](https://www.apmonitor.com/pdc/index.php/Main/QuizTransientBalanceEquations) 2 | - Scripts Located: 1 3 | - Page Description: Learning assessment on transient balance equations with an introduction to conserved quantities such as mass, energy, and momentum. 4 | -------------------------------------------------------------------------------- /Module_10/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Controller Design](https://www.apmonitor.com/pdc/index.php/Main/QuizControlDesign) 2 | - Scripts Located: 0 3 | - Page Description: Learning assessment on controller design. Controller design is the planning of sensors, actuators, and algorithms to automatically regulate a process. 4 | -------------------------------------------------------------------------------- /Module_18/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Transmitter Design Exercise](https://www.apmonitor.com/pdc/index.php/Main/SensorDesign) 2 | - Scripts Located: 0 3 | - Page Description: Sensors convert process conditions into either analog or digital signals. Flow, presssure, level, temperature, and concentration sensors are reviewed. 4 | -------------------------------------------------------------------------------- /Module_30/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Controller Stability Analysis](https://www.apmonitor.com/pdc/index.php/Main/StabilityAnalysis) 2 | - Scripts Located: 2 3 | - Page Description: Stability analysis is finding the range of controller gains that lead to a stabilizing controller. There are multiple methods to compute this range. 4 | -------------------------------------------------------------------------------- /Module_30/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Stability Analysis](https://www.apmonitor.com/pdc/index.php/Main/StabilityAnalysisExercises) 2 | - Scripts Located: 4 3 | - Page Description: Stability analysis is finding the range of controller gains that lead to a stabilizing controller. There are multiple methods to compute this range. 4 | -------------------------------------------------------------------------------- /Module_14/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab PI Control Tuning](https://www.apmonitor.com/pdc/index.php/Main/TCLabPIControlTuning) 2 | - Scripts Located: 6 3 | - Page Description: TCLab with proportional integral (PI) control eliminates offset between the setpoint and measured temperature. Use IMC and ITAE tuning and compare the response 4 | -------------------------------------------------------------------------------- /Module_17/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Valve Design for Flow Control](https://www.apmonitor.com/pdc/index.php/Main/FlowValves) 2 | - Scripts Located: 0 3 | - Page Description: Valves regulate fluid flow and are often the final control element for a process control system to maintain pressure, temperature, composition, or other quantities. 4 | -------------------------------------------------------------------------------- /Module_11/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Proportional-only Control](https://www.apmonitor.com/pdc/index.php/Main/ProportionalControl) 2 | - Scripts Located: 0 3 | - Page Description: Proportional-only control is a less common variant of PID control that does not have an integral or derivative term. It is typically used with integrating processes. 4 | -------------------------------------------------------------------------------- /Module_12/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Proportional Integral (PI) Control](https://www.apmonitor.com/pdc/index.php/Main/QuizProportionalIntegralControl) 2 | - Scripts Located: 1 3 | - Page Description: Learning assessment on proportional integral (PI) control as the most common variant of Proportional Integral Derivative (PID) Control. 4 | -------------------------------------------------------------------------------- /Module_05/3_Assignment/LinearizeDiffEqns_1.py: -------------------------------------------------------------------------------- 1 | # analytic solution 2 | import sympy as sp 3 | sp.init_printing() 4 | # define symbols 5 | v,u = sp.symbols(['v','u']) 6 | Fp,rho,Cd,A,m = sp.symbols(['Fp','rho','Cd','A','m']) 7 | # define equation 8 | eqn = Fp*u/m - rho*A*Cd*v**2/(2*m) 9 | 10 | print(sp.diff(eqn,v)) 11 | print(sp.diff(eqn,u)) -------------------------------------------------------------------------------- /Module_05/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Linearization of Differential Equations](https://www.apmonitor.com/pdc/index.php/Main/LinearizeDiffEqns) 2 | - Scripts Located: 4 3 | - Page Description: Exercises on linearization of nonlinear differential equations. These exercises take the gradient of a nonlinear function with respect to all variables. 4 | -------------------------------------------------------------------------------- /Module_11/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab Proportional-only Control](https://www.apmonitor.com/pdc/index.php/Main/TCLabPOnlyControl) 2 | - Scripts Located: 3 3 | - Page Description: TCLab with proportional-only control has offset between the setpoint and measured temperature. The purpose of this lab activity is to quantify and verify the offset. 4 | -------------------------------------------------------------------------------- /Module_36/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Linear Programming with Python](https://www.apmonitor.com/pdc/index.php/Main/LinearProgramming) 2 | - Scripts Located: 7 3 | - Page Description: Linear Programming (LP) has a linear objective function, equality, and inequality constraints. Popular methods to solve LP problems are interior point and simplex methods 4 | -------------------------------------------------------------------------------- /Module_18/4_TCLab/TCLabSensor_1.py: -------------------------------------------------------------------------------- 1 | // Arduino code tclab.ino 2 | const int pinT1 = 0; 3 | const int pinT2 = 2; 4 | float mV = 0.0; 5 | float degC = 0.0; 6 | for (int i = 0; i < n; i++) { 7 | mV = (float) analogRead(pinT1) * (3300.0/1024.0); 8 | degC = degC + (mV - 500.0)/10.0; 9 | } 10 | degC = degC / float(n); 11 | Serial.println(degC); -------------------------------------------------------------------------------- /Module_36/1_Topic/LinearProgramming_3.py: -------------------------------------------------------------------------------- 1 | # solve with SciPy 2 | from scipy.optimize import linprog 3 | c = [-100, -125] 4 | A = [[3, 6], [8, 4]] 5 | b = [30, 44] 6 | x0_bounds = (0, 5) 7 | x1_bounds = (0, 4) 8 | res = linprog(c, A_ub=A, b_ub=b, \ 9 | bounds=(x0_bounds, x1_bounds), 10 | options={"disp": True}) 11 | print(res) -------------------------------------------------------------------------------- /Module_06/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Graphical Method: FOPDT to Step Test](https://www.apmonitor.com/pdc/index.php/Main/FirstOrderFit) 2 | - Scripts Located: 0 3 | - Page Description: A first-order linear system with time delay is a common empirical description of many dynamic processes. Graphical methods are used to fit a first order system to process data. 4 | -------------------------------------------------------------------------------- /Module_30/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab Stability Analysis](https://www.apmonitor.com/pdc/index.php/Main/TCLabStabilityAnalysis) 2 | - Scripts Located: 3 3 | - Page Description: Stability analysis for the Temperature Control Lab is finding the range of controller gains that lead to a stabilizing controller. There are multiple methods to compute this range. 4 | -------------------------------------------------------------------------------- /Module_35/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Optimization Introduction](https://www.apmonitor.com/pdc/index.php/Main/OptimizationIntroduction) 2 | - Scripts Located: 0 3 | - Page Description: Optimization is used in planning, scheduling, and control. This introduction to optimization for Chemical Engineers covers some of the common technologies used in chemical processing. 4 | -------------------------------------------------------------------------------- /Module_07/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Parameter Regression](https://www.apmonitor.com/pdc/index.php/Main/DynamicParameterRegression) 2 | - Scripts Located: 1 3 | - Page Description: A first-order linear system with time delay is a common empirical description of many dynamic processes. Optimized regression methods are used to fit a first order system to process data. 4 | -------------------------------------------------------------------------------- /Module_28/4_TCLab/TCLabSecondOrder_1.py: -------------------------------------------------------------------------------- 1 | # 2nd order step response 2 | def model(T0,t,M,Kp,taus,zeta): 3 | # T0 = initial T 4 | # t = time 5 | # M = magnitude of the step 6 | # Kp = gain 7 | # taus = second order time constant 8 | # zeta = damping factor (zeta>1 for overdamped) 9 | T = ? # fill in the analytic solution 10 | return T -------------------------------------------------------------------------------- /Module_05/2_Quiz/QuizLinearization_1.py: -------------------------------------------------------------------------------- 1 | # analytic solution with Python 2 | import sympy as sp 3 | # define symbols 4 | y,u,d = sp.symbols(['y','u','d']) 5 | # define equation 6 | dydt = 3*y**3-(u**2-sp.sin(u))**(1/3)+sp.log(d) 7 | # partial derivative with respect to u 8 | beta = sp.diff(dydt,u) 9 | # evaluate at steady state condition 10 | print(beta.subs(u,4).evalf()) -------------------------------------------------------------------------------- /Module_11/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Control a Water Tank Level](https://www.apmonitor.com/pdc/index.php/Main/TankLevel) 2 | - Scripts Located: 2 3 | - Page Description: Proportional-only control is a less common variant of PID control that does not have an integral or derivative term. It is typically used with integrating processes such as this tank level control problem. 4 | -------------------------------------------------------------------------------- /Module_12/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Proportional Integral (PI) Control](https://www.apmonitor.com/pdc/index.php/Main/ProportionalIntegralControl) 2 | - Scripts Located: 1 3 | - Page Description: Proportional Integral (PI) control is a common variant of PID control that does not have a derivative term. It is used to remove offset that is commonly found with P-only controllers. 4 | -------------------------------------------------------------------------------- /Module_15/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab PID Control Tuning](https://www.apmonitor.com/pdc/index.php/Main/TCLabPIDControlTuning) 2 | - Scripts Located: 3 3 | - Page Description: TCLab with proportional integral derivative (PID) control tuning. Tuning a PID controller is the process of finding parameters that improve the controller response to disturbances and setpoint changes. 4 | -------------------------------------------------------------------------------- /Module_31/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Feedforward or Cascade Control](https://www.apmonitor.com/pdc/index.php/Main/FeedforwardCascadeControl) 2 | - Scripts Located: 5 3 | - Page Description: Feedforward and cascade control are two control configurations to reject process disturbances. This example problem shows how to implement feedforward and cascade control for level control. 4 | -------------------------------------------------------------------------------- /Module_13/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Proportional Integral Derivative (PID)](https://www.apmonitor.com/pdc/index.php/Main/ProportionalIntegralDerivative) 2 | - Scripts Located: 1 3 | - Page Description: Proportional Integral Derivative (PID) control is the most commonly used controller in practice. This demonstrates how to obtain tuning values for a PID controller from step test data. 4 | -------------------------------------------------------------------------------- /Module_17/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Valve Design Exercise](https://www.apmonitor.com/pdc/index.php/Main/ValveDesign) 2 | - Scripts Located: 2 3 | - Page Description: Valve exercise to design the size and type of valve for regulator control. Fluid flow is often the final control element for a process control system to maintain pressure, temperature, composition, or other quantities. 4 | -------------------------------------------------------------------------------- /Module_24/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Laplace Transform Applications](https://www.apmonitor.com/pdc/index.php/Main/LaplaceApplications) 2 | - Scripts Located: 2 3 | - Page Description: Express functions or equations in Laplace form. The Laplace exercises are problem that require knowledge of Laplace transforms to develop analytic expressions important in process dynamics and control. 4 | -------------------------------------------------------------------------------- /Module_28/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Optimization Method: SOPDT to Data](https://www.apmonitor.com/pdc/index.php/Main/SecondOrderOptimization) 2 | - Scripts Located: 2 3 | - Page Description: A second-order linear system with time delay is a common empirical description of many dynamic processes. This tutorial demonstrates how to fit an SOPDT model to data with optimization techniques. 4 | -------------------------------------------------------------------------------- /Module_29/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Dynamic Simulation in Python](https://www.apmonitor.com/pdc/index.php/Main/ModelSimulation) 2 | - Scripts Located: 2 3 | - Page Description: Three methods to represent differential equations are (1) transfer functions, (2) state space, and (3) semi-explicit differential equation forms. Python is used to simulate a step response in these three forms. 4 | -------------------------------------------------------------------------------- /Module_26/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [State Space Stirred Reactor](https://www.apmonitor.com/pdc/index.php/Main/ReactorStateSpace) 2 | - Scripts Located: 2 3 | - Page Description: Case study on a stirred tank reactor linearization and modeling in Python. The exercise involves creating a dynamic model based on balance equations, linearizing, and fitting the equations into state space form. 4 | -------------------------------------------------------------------------------- /Module_14/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Automation of Liquid Level Control](https://www.apmonitor.com/pdc/index.php/Main/LevelControl) 2 | - Scripts Located: 3 3 | - Page Description: Case study on a liquid level in a dual gravity drained tank with a PI or PID controller in Python. The exercise involves creating a dynamic first-order model, obtaining tuning parameters, and tuning the controller. 4 | -------------------------------------------------------------------------------- /Module_24/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Laplace Transforms](https://www.apmonitor.com/pdc/index.php/Main/LaplaceTransforms) 2 | - Scripts Located: 1 3 | - Page Description: Transfer functions in the Laplace domain help analyze dynamic systems. This introduction shows how to transform a linear differential equation into the Laplace domain and reposition the variables to create a transfer function. 4 | -------------------------------------------------------------------------------- /Module_25/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Transfer Functions](https://www.apmonitor.com/pdc/index.php/Main/TransferFunctions) 2 | - Scripts Located: 2 3 | - Page Description: Transfer functions in the Laplace domain help analyze dynamic systems. This introduction shows how to transform a linear differential equation into the Laplace domain and reposition the variables to create a transfer function. 4 | -------------------------------------------------------------------------------- /Module_07/2_Quiz/README.md: -------------------------------------------------------------------------------- 1 | ### [Quiz on Parameter Regression](https://www.apmonitor.com/pdc/index.php/Main/QuizFirstOrderRegression) 2 | - Scripts Located: 1 3 | - Page Description: Learning assessment on optimizing a first-order plus time-delay model to fit data. The model is fit to data by adjusting three parameters to minimize the sum of squared errors between the predicted and measured values. 4 | -------------------------------------------------------------------------------- /Module_16/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Maintain Glucose in Type-I Diabetic](https://www.apmonitor.com/pdc/index.php/Main/DiabeticBloodGlucose) 2 | - Scripts Located: 2 3 | - Page Description: Case study on control of glucose level in a Type-I diabetic with a PID controller in Python. The exercise involves creating a dynamic first-order model, obtaining tuning parameters, and tuning the controller. 4 | -------------------------------------------------------------------------------- /Module_15/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Temperature Control of a Stirred Reactor](https://www.apmonitor.com/pdc/index.php/Main/StirredReactor) 2 | - Scripts Located: 2 3 | - Page Description: Case study on temperature control in a stirred tank reactor with a PI or PID controller in Python. The exercise involves creating a dynamic first-order model, obtaining tuning parameters, and tuning the controller. 4 | -------------------------------------------------------------------------------- /Module_39/README.md: -------------------------------------------------------------------------------- 1 | ## Module 39 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Quiz 5 | - [Quiz on Machine Learning Classification](https://www.apmonitor.com/pdc/index.php/Main/QuizMachineLearning): 0 scripts. Description: Learning assessment on machine learning classification. 6 | -------------------------------------------------------------------------------- /Module_05/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Linearization of Differential Equations](https://www.apmonitor.com/pdc/index.php/Main/ModelLinearization) 2 | - Scripts Located: 3 3 | - Page Description: Linearization is the process of taking the gradient of a nonlinear function with respect to all variables. It is required for certain types of analysis such as a Bode plot, Laplace transforms, and for State Space analysis. 4 | -------------------------------------------------------------------------------- /Module_07/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Optimization Method: FOPDT to Data](https://www.apmonitor.com/pdc/index.php/Main/FirstOrderOptimization) 2 | - Scripts Located: 2 3 | - Page Description: A first-order linear system with time delay is a common empirical description of many dynamic processes. This tutorial demonstrates how to fit an FOPDT model to data or a more complex simulation with optimization techniques. 4 | -------------------------------------------------------------------------------- /Module_25/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Transfer Functions in Block Diagrams](https://www.apmonitor.com/pdc/index.php/Main/TransferFunctionBlockDiagrams) 2 | - Scripts Located: 0 3 | - Page Description: Transfer functions (Laplace domain) help analyze dynamic systems. Exercises are to transform a linear differential equation into the Laplace domain and reposition the variables to create a transfer function. 4 | -------------------------------------------------------------------------------- /Module_26/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [State Space Models](https://www.apmonitor.com/pdc/index.php/Main/StateSpaceModel) 2 | - Scripts Located: 1 3 | - Page Description: Linear Time Invariant (LTI) state space models are a linear representation of a dynamic system in either discrete or continuous time. Putting a model into state space form is the basis for many methods in process dynamics and control analysis. 4 | -------------------------------------------------------------------------------- /Module_27/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Graphical Method: Second Order Underdamped](https://www.apmonitor.com/pdc/index.php/Main/SecondOrderApplications) 2 | - Scripts Located: 1 3 | - Page Description: Transfer functions (Laplace domain) help analyze dynamic systems. Exercises are to transform a linear differential equation into the Laplace domain and reposition the variables to create a transfer function. 4 | -------------------------------------------------------------------------------- /Module_27/4_TCLab/README.md: -------------------------------------------------------------------------------- 1 | ### [TCLab On/Off Control](https://www.apmonitor.com/pdc/index.php/Main/TCLabOnOffControl) 2 | - Scripts Located: 3 3 | - Page Description: On/off control is used in most cooling and heating applications where the actuator can only be in an On of Off state. The temperature control lab shows the application of On/Off control and fits a 2nd order model to the closed-loop response. 4 | -------------------------------------------------------------------------------- /Module_26/2_Quiz/QuizStateSpace_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | A = [[-0.2,-1,0.6],[5.0,1.0,-0.7],[1.0,0,-3.0]] 3 | B = [[1],[0],[0]]; C = [0.5,0.5,0.5]; D = [0] 4 | print(np.linalg.eig(A)[0]) 5 | 6 | from scipy import signal 7 | sys1 = signal.StateSpace(A,B,C,D) 8 | t1,y1 = signal.step(sys1) 9 | 10 | import matplotlib.pyplot as plt 11 | plt.figure(1) 12 | plt.plot(t1,y1,'r-',label='y') 13 | plt.legend() 14 | plt.xlabel('Time') 15 | plt.show() -------------------------------------------------------------------------------- /Module_02/2_Quiz/QuizSolveODEs_2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.integrate import odeint 3 | n=31 # total time points 4 | def model(y,t): 5 | return -0.3 * y 6 | y=np.empty(n); t=np.empty(n) 7 | for i in range(n): 8 | if i==0: 9 | t[i] = 0.0 # initial time 10 | y[i] = 5 # initial condition 11 | else: 12 | # integrate forward with time-step 0.1 13 | t[i] = t[i-1]+0.1 14 | y[i] = odeint(model,y[i-1],[t[i-1],t[i]])[-1] -------------------------------------------------------------------------------- /Module_28/4_TCLab/TCLabSecondOrder_4.py: -------------------------------------------------------------------------------- 1 | # 2nd order step response 2 | def model(y0,t,M,Kp,taus,zeta): 3 | # y0 = initial y 4 | # t = time 5 | # M = magnitude of the step 6 | # Kp = gain 7 | # taus = second order time constant 8 | # zeta = damping factor (zeta>1 for overdamped) 9 | a = np.exp(-zeta*t/taus) 10 | b = np.sqrt(zeta**2-1.0) 11 | c = (t/taus)*b 12 | y = Kp * M * (1.0 - a * (np.cosh(c)+(zeta/b)*np.sinh(c))) + y0 13 | return y -------------------------------------------------------------------------------- /Module_27/4_TCLab/TCLabOnOffControl_2.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import pandas as pd 3 | data = pd.read_csv('data.csv') 4 | plt.figure(figsize=(10,7)) 5 | ax=plt.subplot(2,1,1); ax.grid() 6 | plt.plot(data['Time'],data['Q1'],'b-',label=r'$Q_1$ (%)') 7 | plt.legend(); plt.ylabel('Heater') 8 | ax=plt.subplot(2,1,2); ax.grid() 9 | plt.plot(data['Time'],data['T1'],'r-',label=r'$T_1$ $(^oC)$') 10 | plt.legend(); plt.xlabel('Time (sec)'); plt.ylabel('Temperature') 11 | plt.show() -------------------------------------------------------------------------------- /Module_36/1_Topic/LinearProgramming_4.py: -------------------------------------------------------------------------------- 1 | from gekko import GEKKO 2 | m = GEKKO(remote=False) 3 | c = [100, 125] 4 | A = [[3, 6], [8, 4]] 5 | b = [30, 44] 6 | x = m.qobj(c,otype='max') 7 | m.axb(A,b,x=x,etype='<') 8 | x[0].lower=0; x[0].upper=5 9 | x[1].lower=0; x[1].upper=4 10 | m.options.solver = 1 11 | m.solve(disp=True) 12 | print ('Product 1 (x1): ' + str(x[0].value[0])) 13 | print ('Product 2 (x2): ' + str(x[1].value[0])) 14 | print ('Profit : ' + str(m.options.objfcnval)) -------------------------------------------------------------------------------- /Module_36/1_Topic/LinearProgramming_1.py: -------------------------------------------------------------------------------- 1 | from gekko import GEKKO 2 | m = GEKKO() 3 | x1 = m.Var(lb=0, ub=5) # Product 1 4 | x2 = m.Var(lb=0, ub=4) # Product 2 5 | m.Maximize(100*x1+125*x2) # Profit function 6 | m.Equation(3*x1+6*x2<=30) # Units of A 7 | m.Equation(8*x1+4*x2<=44) # Units of B 8 | m.solve(disp=False) 9 | p1 = x1.value[0]; p2 = x2.value[0] 10 | print ('Product 1 (x1): ' + str(p1)) 11 | print ('Product 2 (x2): ' + str(p2)) 12 | print ('Profit : ' + str(100*p1+125*p2)) -------------------------------------------------------------------------------- /Module_36/2_Quiz/QuizLinearProgramming_2.py: -------------------------------------------------------------------------------- 1 | from gekko import GEKKO 2 | m = GEKKO() 3 | x1 = m.Var(lb=2, ub=5) # Product 1 4 | x2 = m.Var(lb=3, ub=4) # Product 2 5 | m.Minimize(100*x1+125*x2) # Profit function 6 | m.Equation(3*x1+6*x2<=30) # Units of A 7 | m.Equation(8*x1+4*x2<=44) # Units of B 8 | m.solve(disp=False) 9 | p1 = x1.value[0]; p2 = x2.value[0] 10 | print ('Product 1 (x1): ' + str(p1)) 11 | print ('Product 2 (x2): ' + str(p2)) 12 | print ('Profit : ' + str(100*p1+125*p2)) -------------------------------------------------------------------------------- /Module_02/1_Topic/SolveDifferentialEquations_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.integrate import odeint 3 | import matplotlib.pyplot as plt 4 | 5 | # function that returns dy/dt 6 | def model(y,t): 7 | dydt = -y + 1.0 8 | return dydt 9 | 10 | # initial condition 11 | y0 = 0 12 | 13 | # time points 14 | t = np.linspace(0,5) 15 | 16 | # solve ODE 17 | y = odeint(model,y0,t) 18 | 19 | # plot results 20 | plt.plot(t,y) 21 | plt.xlabel('time') 22 | plt.ylabel('y(t)') 23 | plt.show() -------------------------------------------------------------------------------- /Module_02/1_Topic/SolveDifferentialEquations_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.integrate import odeint 3 | import matplotlib.pyplot as plt 4 | 5 | # function that returns dy/dt 6 | def model(y,t): 7 | k = 0.3 8 | dydt = -k * y 9 | return dydt 10 | 11 | # initial condition 12 | y0 = 5 13 | 14 | # time points 15 | t = np.linspace(0,20) 16 | 17 | # solve ODE 18 | y = odeint(model,y0,t) 19 | 20 | # plot results 21 | plt.plot(t,y) 22 | plt.xlabel('time') 23 | plt.ylabel('y(t)') 24 | plt.show() -------------------------------------------------------------------------------- /Module_30/3_Assignment/StabilityAnalysisExercises_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy import signal 3 | import matplotlib.pyplot as plt 4 | 5 | name = 'Problem 1A' 6 | num = [4.0] 7 | p1 = [1,-1] 8 | p2 = [1,2] 9 | p3 = [1,3] 10 | den = np.convolve(p1,p1) 11 | den = np.convolve(den,p2) 12 | den = np.convolve(den,p3) 13 | sys = signal.TransferFunction(num, den) 14 | t,y = signal.step(sys) 15 | 16 | plt.figure(1) 17 | plt.plot(t,y,'k-') 18 | plt.legend([name],loc='best') 19 | plt.xlabel('Time') 20 | plt.show() -------------------------------------------------------------------------------- /Module_21/1_Topic/ArduinoModeling_2.py: -------------------------------------------------------------------------------- 1 | % save as step_test.m 2 | clear all; close all; clc 3 | 4 | Q = 100.0; % Percent Heater (0-100%) 5 | TK0 = 23.0 + 273.15; % Initial temperature 6 | n = 60*10+1; % Number of second time points (10min) 7 | time = linspace(0,n-1,n); % Time vector 8 | [time,TK] = ode23(@(t,x)heat(t,x,Q),time,TK0); % Integrate ODE 9 | 10 | % Plot results 11 | figure(1) 12 | plot(time/60.0,TK-273.15,'b-') 13 | ylabel('Temperature (degC)') 14 | xlabel('Time (min)') 15 | legend('Step Test (0-100% heater)') -------------------------------------------------------------------------------- /Module_36/1_Topic/LinearProgramming_2.py: -------------------------------------------------------------------------------- 1 | m = py.gekko.GEKKO(); 2 | x1 = m.Var(pyargs('lb',0,'ub',5)); % Product 1 3 | x2 = m.Var(pyargs('lb',0,'ub',4)); % Product 2 4 | m.Maximize(100*x1+125*x2); % Profit function 5 | m.Equation(3*x1+6*x2<=30); % Units of A 6 | m.Equation(8*x1+4*x2<=44); % Units of B 7 | m.solve(pyargs('disp',false)); 8 | p1 = x1.VALUE{1}; 9 | p2 = x2.VALUE{1}; 10 | disp(['Product 1 (x1): ', num2str(p1)]) 11 | disp(['Product 2 (x2): ', num2str(p2)]) 12 | disp(['Profit : ', num2str(100*p1+125*p2)]) -------------------------------------------------------------------------------- /Module_02/3_Assignment/README.md: -------------------------------------------------------------------------------- 1 | ### [Simulate HIV Infection](https://www.apmonitor.com/pdc/index.php/Main/SimulateHIV) 2 | - Scripts Located: 0 3 | - Page Description: A beginning tutorial on solving differential equations with numerical methods in Python with HIV simulation in a human body. 4 | ### [COVID-19 SEIR Simulation](https://www.apmonitor.com/pdc/index.php/Main/SimulateCOVID19) 5 | - Scripts Located: 5 6 | - Page Description: Simulate COVID-19 spread on a university campus to determine the effect of social distancing measures. 7 | -------------------------------------------------------------------------------- /Module_30/3_Assignment/StabilityAnalysisExercises_4.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy import signal 3 | import matplotlib.pyplot as plt 4 | 5 | a = 0.08*0.75*30*0.7 6 | n = 5 7 | Kc = np.linspace(0.78,8.5,n) 8 | tm = np.linspace(0,100,1000) 9 | for Kci in Kc: 10 | num = [a*0.1*Kci,a*Kci] 11 | den = [0.2,2.1,0.9,-1+1.26*Kci] 12 | sys = signal.TransferFunction(num,den) 13 | t,y = signal.step(sys,T=tm) 14 | plt.plot(t,y,label='Kc='+str(Kci)) 15 | plt.legend(loc='best') 16 | plt.xlim([0,100]) 17 | plt.ylim([-2,5]) 18 | plt.show() -------------------------------------------------------------------------------- /Module_36/2_Quiz/QuizLinearProgramming_1.py: -------------------------------------------------------------------------------- 1 | # currently producer optimization, change to consumer optimization 2 | from gekko import GEKKO 3 | m = GEKKO() 4 | x1 = m.Var(lb=0, ub=5) # Product 1 5 | x2 = m.Var(lb=0, ub=4) # Product 2 6 | m.Maximize(100*x1+125*x2) # Profit function 7 | m.Equation(3*x1+6*x2<=30) # Units of A 8 | m.Equation(8*x1+4*x2<=44) # Units of B 9 | m.solve(disp=False) 10 | p1 = x1.value[0]; p2 = x2.value[0] 11 | print ('Product 1 (x1): ' + str(p1)) 12 | print ('Product 2 (x2): ' + str(p2)) 13 | print ('Profit : ' + str(100*p1+125*p2)) -------------------------------------------------------------------------------- /Module_31/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Cascade Control](https://www.apmonitor.com/pdc/index.php/Main/CascadeControl) 2 | - Scripts Located: 0 3 | - Page Description: Cascade control is implemented as a hierarchy of control loops that successively provide set points to lower levels. 4 | ### [Feedforward Control](https://www.apmonitor.com/pdc/index.php/Main/FeedforwardControl) 5 | - Scripts Located: 0 6 | - Page Description: Feedforward control is implemented to reject disturbances and anticipate the effect on the systems before it shows up as a deviation from the set point. 7 | -------------------------------------------------------------------------------- /Module_38/1_Topic/NonlinearProgramming_2.py: -------------------------------------------------------------------------------- 1 | from gekko import GEKKO 2 | m = GEKKO(remote=False) 3 | 4 | # create variables 5 | x1,x2,x3,x4 = m.Array(m.Var,4,lb=1,ub=5) 6 | 7 | # initial values 8 | x1.value = 1; x2.value = 5; x3.value = 5; x4.value = 1 9 | 10 | m.Equation(x1*x2*x3*x4>=25) 11 | m.Equation(x1**2+x2**2+x3**2+x4**2==40) 12 | m.Minimize(x1*x4*(x1+x2+x3)+x3) 13 | m.solve(disp=False) 14 | 15 | print('Results') 16 | print('x1: ' + str(x1.value[0])); print('x2: ' + str(x2.value[0])) 17 | print('x3: ' + str(x3.value[0])); print('x4: ' + str(x4.value[0])) -------------------------------------------------------------------------------- /Module_26/4_TCLab/TCLabStateSpace_2.py: -------------------------------------------------------------------------------- 1 | Am = np.zeros((4,4)) 2 | Bm = np.zeros((4,2)) 3 | Cm = np.zeros((2,4)) 4 | Dm = np.zeros((2,2)) 5 | 6 | T0 = Ta 7 | c1 = U.value[0]*A 8 | c2 = 4*eps*sigma*A*T0**3 9 | c3 = Us.value[0]*As 10 | c4 = 4*eps*sigma*As*T0**3 11 | c5 = mass*Cp 12 | c6 = 1/tau.value[0] 13 | 14 | Am[0,0] = -(c1+c2+c3+c4)/c5 15 | Am[0,1] = (c3+c4)/c5 16 | 17 | Am[1,0] = 18 | Am[1,1] = 19 | 20 | Am[2,0] = c6 21 | Am[2,2] = 22 | 23 | Am[3,1] = 24 | Am[3,3] = -c6 25 | 26 | Bm[0,0] = alpha1.value[0]/c5 27 | Bm[1,1] = 28 | 29 | Cm[0,2] = 1 30 | Cm[1,3] = -------------------------------------------------------------------------------- /Module_38/2_Quiz/QuizNonlinearProgramming_3.py: -------------------------------------------------------------------------------- 1 | from gekko import GEKKO 2 | m = GEKKO(remote=False) 3 | 4 | # create variables 5 | x1,x2,x3,x4 = m.Array(m.Var,4,lb=1,ub=5) 6 | 7 | # initial guess values 8 | x1.value = 1; x2.value = 5; x3.value = 5; x4.value = 1 9 | 10 | m.Equation(x1*x2*x3*x4>=25) 11 | m.Equation(x1**2+x2**2+x3**2+x4**2==40) 12 | m.Minimize(x1*x4*(x1+x2+x3)+x3) 13 | m.solve(disp=False) 14 | 15 | print('Results') 16 | print('x1: ' + str(x1.value[0])); print('x2: ' + str(x2.value[0])) 17 | print('x3: ' + str(x3.value[0])); print('x4: ' + str(x4.value[0])) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Process Dynamics and Control 2 | 3 | Source code for the [Process Dynamics and Control Course](https://apmonitor.com/pdc). The archive is organized by learning module from the [course schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) with source code by Topic, Quiz, Assignment, and Temperature Control Lab activity. The [get_source.py](get_source.py) Python program retrieves the lastest version of the source code from each page listed in [pages.csv](pages.csv). 4 | 5 | ![PDC Course Overview](https://apmonitor.com/pdc/uploads/Main/pdc_overview.png) 6 | 7 | -------------------------------------------------------------------------------- /Module_27/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Second Order Systems](https://www.apmonitor.com/pdc/index.php/Main/SecondOrderSystems) 2 | - Scripts Located: 1 3 | - Page Description: A second-order linear system is a common description of many dynamic processes. Overdamped, critically damped, and underdamped second order systems are three classes. 4 | ### [Graphically Fit Second Order Response](https://www.apmonitor.com/pdc/index.php/Main/SecondOrderGraphical) 5 | - Scripts Located: 1 6 | - Page Description: An oscillating second-order linear system describes many underdamped dynamic processes. 7 | -------------------------------------------------------------------------------- /Module_02/4_TCLab/TCLabSim_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | 6 | n = 300 # Number of second time points (5 min) 7 | tm = np.linspace(0,n,n+1) # Time values 8 | 9 | # data 10 | lab = tclab.TCLab() 11 | T1 = [lab.T1] 12 | lab.Q1(50) 13 | for i in range(n): 14 | time.sleep(1) 15 | print(lab.T1) 16 | T1.append(lab.T1) 17 | lab.close() 18 | 19 | # Plot results 20 | plt.figure(1) 21 | plt.plot(tm,T1,'r.',label='Measured') 22 | plt.ylabel('Temperature (degC)') 23 | plt.xlabel('Time (sec)') 24 | plt.legend() 25 | plt.show() -------------------------------------------------------------------------------- /Module_24/3_Assignment/LaplaceApplications_2.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | 4 | K = 1 5 | tau = 0.5 6 | n = 81 7 | t = np.linspace(0,8,n) 8 | s1 = np.zeros(n) 9 | s1[11:] = 1.0 10 | s2 = np.zeros(n) 11 | s2[51:] = 1.0 12 | 13 | y = 3*K*(1-np.exp(-(t-1)/tau))*s1 \ 14 | -3*K*(1-np.exp(-(t-5)/tau))*s2 15 | 16 | plt.figure(1) 17 | plt.plot([0,1,1.001,5,5.001,8],[0,0,3,3,0,0],'b-',linewidth=2) 18 | plt.plot(t,y,'r--') 19 | plt.ylabel('y(t)') 20 | plt.xlabel('time (t)') 21 | plt.legend(['u(t)','y(t)']) 22 | plt.grid() 23 | plt.savefig('fig1.png') 24 | 25 | plt.show() -------------------------------------------------------------------------------- /Module_03/4_TCLab/TCLabConvection_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | 6 | n = 300 # Number of second time points (5 min) 7 | tm = np.linspace(0,n,n+1) # Time values 8 | 9 | # data 10 | lab = tclab.TCLab() 11 | T1 = [lab.T1] 12 | lab.Q1(50) 13 | for i in range(n): 14 | time.sleep(1) 15 | print(lab.T1) 16 | T1.append(lab.T1) 17 | lab.close() 18 | 19 | # Plot results 20 | plt.figure(1) 21 | plt.plot(tm,T1,'r.',label='Measured') 22 | plt.ylabel('Temperature (degC)') 23 | plt.xlabel('Time (sec)') 24 | plt.legend() 25 | plt.show() -------------------------------------------------------------------------------- /Module_04/4_TCLab/TCLabRadiative_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | 6 | n = 300 # Number of second time points (5 min) 7 | tm = np.linspace(0,n,n+1) # Time values 8 | 9 | # data 10 | lab = tclab.TCLab() 11 | T1 = [lab.T1] 12 | lab.Q1(50) 13 | for i in range(n): 14 | time.sleep(1) 15 | print(lab.T1) 16 | T1.append(lab.T1) 17 | lab.close() 18 | 19 | # Plot results 20 | plt.figure(1) 21 | plt.plot(tm,T1,'r.',label='Measured') 22 | plt.ylabel('Temperature (degC)') 23 | plt.xlabel('Time (sec)') 24 | plt.legend() 25 | plt.show() -------------------------------------------------------------------------------- /Module_27/4_TCLab/TCLabOnOffControl_1.py: -------------------------------------------------------------------------------- 1 | import tclab 2 | import time 3 | a = tclab.TCLab() # connect to TCLab 4 | fid = open('data.csv','w') 5 | fid.write('Time,Q1,T1\n') 6 | fid.write('0,0,'+str(a.T1)+'\n') 7 | fid.close() 8 | for i in range(240): # 4 minute test 9 | time.sleep(1) 10 | T1 = a.T1 # temperature 11 | Q1 = 100 if T1<=40.0 else 0 # On/Off Control 12 | a.Q1(Q1) # set heater 13 | print('Time: '+str(i)+' Q1: '+str(Q1)+' T1 (SP=40): '+str(T1)) 14 | fid = open('data.csv','a') 15 | fid.write(str(i)+','+str(Q1)+','+str(T1)+'\n') 16 | fid.close() 17 | a.close() -------------------------------------------------------------------------------- /Module_38/2_Quiz/QuizNonlinearProgramming_4.py: -------------------------------------------------------------------------------- 1 | from gekko import GEKKO 2 | m = GEKKO(remote=False) 3 | 4 | # create variables 5 | x1,x2,x3,x4 = m.Array(m.Var,4,lb=1,ub=5) 6 | 7 | # initial guess values 8 | x1.value = 1; x2.value = 5; x3.value = 5; x4.value = 1 9 | 10 | m.Equation(x1*x2*x3*x4>=25) 11 | m.Equation(x1**2+x2**2+x3**2+x4**2==40) 12 | m.Equation(x1+x4>=3) 13 | m.Minimize(x1*x4*(x1+x2+x3)+x3) 14 | m.solve(disp=False) 15 | 16 | print('Results') 17 | print('x1: ' + str(x1.value[0])); print('x2: ' + str(x2.value[0])) 18 | print('x3: ' + str(x3.value[0])); print('x4: ' + str(x4.value[0])) -------------------------------------------------------------------------------- /Module_36/1_Topic/LinearProgramming_5.py: -------------------------------------------------------------------------------- 1 | # solve with GEKKO and sparse matrices 2 | import numpy as np 3 | from gekko import GEKKO 4 | m = GEKKO(remote=False) 5 | # [[row indices],[column indices],[values]] 6 | A_sparse = [[1,1,2,2],[1,2,1,2],[3,6,8,4]] 7 | # [[row indices],[values]] 8 | b_sparse = [[1,2],[30,44]] 9 | x = m.axb(A_sparse,b_sparse,etype='<',sparse=True) 10 | # [[row indices],[values]] 11 | c_sparse = [[1,2],[100,125]] 12 | m.qobj(c_sparse,x=x,otype='max',sparse=True) 13 | x[0].lower=0; x[0].upper=5 14 | x[1].lower=0; x[1].upper=4 15 | m.solve(disp=True) 16 | print(m.options.OBJFCNVAL) 17 | print('x: ' + str(x)) -------------------------------------------------------------------------------- /Module_23/README.md: -------------------------------------------------------------------------------- 1 | ## Module 23 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Arduino Temperature PID Control](https://www.apmonitor.com/pdc/index.php/Main/ArduinoControl): 2 scripts. Description: Implement Temperature Control and Test Set Point Changes with an Arduino Device 6 | ### TCLab 7 | - [Interacting PID Control](https://www.apmonitor.com/pdc/index.php/Main/ArduinoControl2): 3 scripts. Description: Implement Temperature PID Control with Feedforward and Test Set Point Changes with Arduino Data 8 | -------------------------------------------------------------------------------- /Module_37/README.md: -------------------------------------------------------------------------------- 1 | ## Module 37 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Refinery Optimization](https://www.apmonitor.com/pdc/index.php/Main/RefineryOptimization): 0 scripts. Description: Refineries are optimized with either linear or nonlinear programming solvers to maximize revenue and meet customer contracts for production. 6 | ### Quiz 7 | - [Quiz on Optimization](https://www.apmonitor.com/pdc/index.php/Main/QuizSchedulingControl): 1 scripts. Description: Learning assessment on optimization. 8 | -------------------------------------------------------------------------------- /Module_21/README.md: -------------------------------------------------------------------------------- 1 | ## Module 21 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Arduino Dynamic Temperature Modeling](https://www.apmonitor.com/pdc/index.php/Main/ArduinoModeling): 5 scripts. Description: Dynamic Temperature Response of a Heater and Temperature Sensor with an Arduino 6 | ### TCLab 7 | - [Arduino Dynamic Response: 2 Heaters](https://www.apmonitor.com/pdc/index.php/Main/ArduinoModeling2): 5 scripts. Description: Dynamic Temperature Response of a Heater and Temperature Sensor with an Arduino 8 | -------------------------------------------------------------------------------- /Module_24/3_Assignment/LaplaceApplications_1.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | 3 | plt.figure(1) 4 | plt.plot([0,1,1.001,5,5.001,8],[0,0,3,3,0,0],'b-',linewidth=2) 5 | plt.ylabel('y(t)') 6 | plt.xlabel('time (t)') 7 | plt.grid() 8 | plt.savefig('fig1.png') 9 | 10 | plt.figure(2) 11 | plt.plot([0,2,4,6,8,10],[0,0,5,5,3,3],'r-',linewidth=2) 12 | plt.ylabel('y(t)') 13 | plt.xlabel('time (t)') 14 | plt.grid() 15 | plt.savefig('fig2.png') 16 | 17 | plt.figure(3) 18 | plt.plot([0,1,1.0001,4,4.0001,6],[0,0,-4,-4,2,8],'g-',linewidth=2) 19 | plt.ylabel('y(t)') 20 | plt.xlabel('time (t)') 21 | plt.grid() 22 | plt.savefig('fig3.png') 23 | plt.show() -------------------------------------------------------------------------------- /Module_18/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [Sensors and Signals for Process Control](https://www.apmonitor.com/pdc/index.php/Main/SensorSignals) 2 | - Scripts Located: 0 3 | - Page Description: Pumps and valves regulate fluid flow and are often the final control element for a process control system. Analog control signals are often sent 3-15 psig levels to valves and sensors return 4-20 mA signals to indicate the fraction of sensor range. 4 | ### [Data Acquisition (DAQ)](https://www.apmonitor.com/pdc/index.php/Main/DataAcquisition) 5 | - Scripts Located: 0 6 | - Page Description: Python tutorial for collecting data for dynamic simulation, estimation, and control 7 | -------------------------------------------------------------------------------- /Module_40/README.md: -------------------------------------------------------------------------------- 1 | ## Module 40 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Model Predictive Control](https://www.apmonitor.com/pdc/index.php/Main/ModelPredictiveControl): 1 scripts. Description: Dynamic control in MATLAB and Python for use in real-time or off-line applications 6 | ### Quiz 7 | - [Quiz on Model Predictive Control](https://www.apmonitor.com/pdc/index.php/Main/QuizModelPredictiveControl): 0 scripts. Description: Learning assessment on model predictive control and when MPC is needed over PID control. 8 | -------------------------------------------------------------------------------- /Module_27/3_Assignment/SecondOrderApplications_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy import signal 3 | import matplotlib.pyplot as plt 4 | from scipy.integrate import odeint 5 | 6 | # Change these values based on graphical fit 7 | Kp = 1.0 8 | taus = 1.0 9 | zeta = 1.0 10 | 11 | # Transfer Function 12 | # Kp / (taus * s**2 + 2 * zeta * taus * s + 1) 13 | num = [Kp] 14 | den = [taus**2,2.0*zeta*taus,1] 15 | sys1 = signal.TransferFunction(num,den) 16 | t1,y1 = signal.step(sys1) 17 | 18 | plt.figure(1) 19 | plt.plot(t1,y1,'b--',linewidth=3,label='Transfer Fcn') 20 | plt.xlabel('Time') 21 | plt.ylabel('Response (y)') 22 | plt.legend(loc='best') 23 | plt.show() -------------------------------------------------------------------------------- /Module_01/README.md: -------------------------------------------------------------------------------- 1 | ## Module 1 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Process Dynamics and Control in Python](https://www.apmonitor.com/pdc/index.php/Main/HomePage): 0 scripts. Description: Python Process Control and Dynamics Course in Chemical Engineering at Brigham Young University 6 | ### Assignment 7 | - [Install Python](https://www.apmonitor.com/pdc/index.php/Main/InstallPython): 1 scripts. Description: Python is a general-purpose programming language. It is used in this course for process dynamics and control. 8 | -------------------------------------------------------------------------------- /Module_05/3_Assignment/LinearizeDiffEqns_2.py: -------------------------------------------------------------------------------- 1 | # numeric solution 2 | from scipy.misc import derivative 3 | m = 700.0 # kg 4 | Cd = 0.24 5 | rho = 1.225 # kg/m^3 6 | A = 5.0 # m^2 7 | Fp = 30.0 # N/%pedal 8 | u = 40.0 # % pedal 9 | v = 50.0 # km/hr (change this for SS condition) 10 | def fv(v): 11 | return Fp*u/m - rho*A*Cd*v**2/(2*m) 12 | def fu(u): 13 | return Fp*u/m - rho*A*Cd*v**2/(2*m) 14 | 15 | print('Approximate Partial Derivatives') 16 | print(derivative(fv,v,dx=1e-4)) 17 | print(derivative(fu,u,dx=1e-4)) 18 | 19 | print('Exact Partial Derivatives') 20 | print(-A*Cd*rho*v/m) # exact d(f(u,v))/dv 21 | print(Fp/m) # exact d(f(u,v))/du -------------------------------------------------------------------------------- /Module_22/README.md: -------------------------------------------------------------------------------- 1 | ## Module 22 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Energy Balance Parameter Estimation](https://www.apmonitor.com/pdc/index.php/Main/ArduinoEstimation): 2 scripts. Description: Parameter Estimation of Temperature Response of Heater and Thermistor on an Arduino 6 | ### TCLab 7 | - [Parameter Regression with Arduino Data](https://www.apmonitor.com/pdc/index.php/Main/ArduinoEstimation2): 10 scripts. Description: Parameter Estimation of Temperature Response of Two Heaters and Two Thermistors with an Arduino 8 | -------------------------------------------------------------------------------- /Module_38/README.md: -------------------------------------------------------------------------------- 1 | ## Module 38 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Nonlinear Programming with Python](https://www.apmonitor.com/pdc/index.php/Main/NonlinearProgramming): 2 scripts. Description: Optimization with Python - Problem-Solving Techniques for Chemical Engineers at Brigham Young University 6 | ### Quiz 7 | - [Quiz on Nonlinear Programming](https://www.apmonitor.com/pdc/index.php/Main/QuizNonlinearProgramming): 4 scripts. Description: Learning assessment on nonlinear programming with the KKT conditions and example problems. 8 | -------------------------------------------------------------------------------- /Module_21/1_Topic/ArduinoModeling_3.py: -------------------------------------------------------------------------------- 1 | % save as heat.m 2 | % define energy balance model 3 | function dTdt = heat(time,x,Q) 4 | % Parameters 5 | Ta = 23 + 273.15; % K 6 | U = 10.0; % W/m^2-K 7 | m = 4.0/1000.0; % kg 8 | Cp = 0.5 * 1000.0; % J/kg-K 9 | A = 12.0 / 100.0^2; % Area in m^2 10 | alpha = 0.01; % W / % heater 11 | eps = 0.9; % Emissivity 12 | sigma = 5.67e-8; % Stefan-Boltzman 13 | 14 | % Temperature State 15 | T = x(1); 16 | 17 | % Nonlinear Energy Balance 18 | dTdt = (1.0/(m*Cp))*(U*A*(Ta-T) ... 19 | + eps * sigma * A * (Ta^4 - T^4) ... 20 | + alpha*Q); 21 | end -------------------------------------------------------------------------------- /Module_23/1_Topic/ArduinoControl_1.py: -------------------------------------------------------------------------------- 1 | import tclab 2 | import time 3 | import numpy as np 4 | from simple_pid import PID 5 | 6 | # Connect to Arduino 7 | a = tclab.TCLab() 8 | 9 | # Create PID controller 10 | pid = PID(Kp=2,Ki=2/136,Kd=0,\ 11 | setpoint=40,sample_time=1.0,output_limits=(0,100)) 12 | 13 | for i in range(300): # 5 minutes (300 sec) 14 | # pid control 15 | OP = pid(a.T1) 16 | a.Q1(OP) 17 | 18 | # print line 19 | print('Heater: ' + str(round(OP,2)) + '%' + \ 20 | ' T PV: ' + str(a.T1) + 'degC' + \ 21 | ' T SP: ' + str(pid.setpoint) + 'degC') 22 | 23 | # wait for next sample time 24 | time.sleep(pid.sample_time) -------------------------------------------------------------------------------- /Module_02/1_Topic/SolveDifferentialEquations_5.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.integrate import odeint 3 | import matplotlib.pyplot as plt 4 | 5 | # function that returns dz/dt 6 | def model(z,t): 7 | dxdt = 3.0 * np.exp(-t) 8 | dydt = -z[1] + 3 9 | dzdt = [dxdt,dydt] 10 | return dzdt 11 | 12 | # initial condition 13 | z0 = [0,0] 14 | 15 | # time points 16 | t = np.linspace(0,5) 17 | 18 | # solve ODE 19 | z = odeint(model,z0,t) 20 | 21 | # plot results 22 | plt.plot(t,z[:,0],'b-',label=r'$\frac{dx}{dt}=3 \; \exp(-t)$') 23 | plt.plot(t,z[:,1],'r--',label=r'$\frac{dy}{dt}=-y+3$') 24 | plt.ylabel('response') 25 | plt.xlabel('time') 26 | plt.legend(loc='best') 27 | plt.show() -------------------------------------------------------------------------------- /Module_18/4_TCLab/TCLabSensor_3.py: -------------------------------------------------------------------------------- 1 | def T(mV): 2 | return round(0.1*mV-50.0,1) 3 | 4 | def mV(T): 5 | return round((T+50.0)*10.0,2) 6 | 7 | print('mV signal at 25 degC: ' + str(mV(25))) 8 | print('mV signal at 80 degC: ' + str(mV(80))) 9 | print('T at 0.5V: ' + str(T(0.5*1000))) 10 | print('T at 1.2V: ' + str(T(1.2*1000))) 11 | 12 | print('Current temperature for T1 and T2 in Celsius and milliVolts') 13 | import tclab 14 | lab = tclab.TCLab() 15 | T1 = lab.T1 16 | T2 = lab.T2 17 | T1mV = (T1+50.0)*10.0 18 | T2mV = (T2+50.0)*10.0 19 | print('T1: '+str(T1)+' degC') 20 | print('T2: '+str(T2)+' degC') 21 | print('T1: '+str(round(T1mV))+' mV') 22 | print('T2: '+str(round(T2mV))+' mV') 23 | lab.close() -------------------------------------------------------------------------------- /Module_36/README.md: -------------------------------------------------------------------------------- 1 | ## Module 36 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Linear Programming with Python](https://www.apmonitor.com/pdc/index.php/Main/LinearProgramming): 7 scripts. Description: Linear Programming (LP) has a linear objective function, equality, and inequality constraints. Popular methods to solve LP problems are interior point and simplex methods 6 | ### Quiz 7 | - [Quiz on Linear Programming](https://www.apmonitor.com/pdc/index.php/Main/QuizLinearProgramming): 2 scripts. Description: Learning assessment on linear programming with simple tutorial problems. 8 | -------------------------------------------------------------------------------- /Module_02/1_Topic/SolveDifferentialEquations_4.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.integrate import odeint 3 | import matplotlib.pyplot as plt 4 | 5 | # function that returns dy/dt 6 | def model(y,t): 7 | # u steps from 0 to 2 at t=10 8 | if t<10.0: 9 | u = 0 10 | else: 11 | u = 2 12 | dydt = (-y + u)/5.0 13 | return dydt 14 | 15 | # initial condition 16 | y0 = 1 17 | 18 | # time points 19 | t = np.linspace(0,40,1000) 20 | 21 | # solve ODE 22 | y = odeint(model,y0,t) 23 | 24 | # plot results 25 | plt.plot(t,y,'r-',label='Output (y(t))') 26 | plt.plot([0,10,10,40],[0,0,2,2],'b-',label='Input (u(t))') 27 | plt.ylabel('values') 28 | plt.xlabel('time') 29 | plt.legend(loc='best') 30 | plt.show() -------------------------------------------------------------------------------- /Module_35/README.md: -------------------------------------------------------------------------------- 1 | ## Module 35 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Optimization Introduction](https://www.apmonitor.com/pdc/index.php/Main/OptimizationIntroduction): 0 scripts. Description: Optimization is used in planning, scheduling, and control. This introduction to optimization for Chemical Engineers covers some of the common technologies used in chemical processing. 6 | ### Quiz 7 | - [Quiz on Multi-Scale Simulation and Optimization](https://www.apmonitor.com/pdc/index.php/Main/QuizOptimization): 0 scripts. Description: Learning assessment on simulation and multi-scale optimization. 8 | -------------------------------------------------------------------------------- /Module_02/1_Topic/SolveDifferentialEquations_2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.integrate import odeint 3 | import matplotlib.pyplot as plt 4 | 5 | # function that returns dy/dt 6 | def model(y,t,k): 7 | dydt = -k * y 8 | return dydt 9 | 10 | # initial condition 11 | y0 = 5 12 | 13 | # time points 14 | t = np.linspace(0,20) 15 | 16 | # solve ODEs 17 | k = 0.1 18 | y1 = odeint(model,y0,t,args=(k,)) 19 | k = 0.2 20 | y2 = odeint(model,y0,t,args=(k,)) 21 | k = 0.5 22 | y3 = odeint(model,y0,t,args=(k,)) 23 | 24 | # plot results 25 | plt.plot(t,y1,'r-',linewidth=2,label='k=0.1') 26 | plt.plot(t,y2,'b--',linewidth=2,label='k=0.2') 27 | plt.plot(t,y3,'g:',linewidth=2,label='k=0.5') 28 | plt.xlabel('time') 29 | plt.ylabel('y(t)') 30 | plt.legend() 31 | plt.show() -------------------------------------------------------------------------------- /Module_28/2_Quiz/QuizSecondOrderOptimization_1.py: -------------------------------------------------------------------------------- 1 | # simulate model with x=[Km,taum,thetam] 2 | def sim_model(x): 3 | # input arguments 4 | Kp = x[0] 5 | taus = x[1] 6 | zeta = x[2] 7 | thetap = x[3] 8 | # storage for model values 9 | xm = np.zeros((ns,2)) # model 10 | # initial condition 11 | xm[0] = xp0 12 | # loop through time steps 13 | for i in range(0,ns-1): 14 | ts = [t[i],t[i+1]] 15 | inputs = (uf,Kp,taus,zeta,thetap) 16 | # turn off warnings 17 | with warnings.catch_warnings(): 18 | warnings.simplefilter("ignore") 19 | # integrate SOPDT model 20 | x = odeint(sopdt,xm[i],ts,args=inputs) 21 | xm[i+1] = x[-1] 22 | y = xm[:,0] 23 | return y -------------------------------------------------------------------------------- /Module_37/2_Quiz/QuizSchedulingControl_1.py: -------------------------------------------------------------------------------- 1 | from gekko import GEKKO 2 | m = GEKKO(remote=False) 3 | paper_width = 8.5 # width of paper 4 | paper_length = 11 # length of paper 5 | x = m.Var(lb=0) # cut out length 6 | box_width = m.Intermediate(paper_width - 2 * x) 7 | box_length = m.Intermediate(paper_length - 2 * x) 8 | box_height = m.Intermediate(x) 9 | Volume = m.Intermediate(box_width * box_length * box_height) 10 | # lower constraint for box width with tabs 11 | m.Equations([box_width > 0,box_length > 0,Volume > 0.01]) 12 | m.Maximize(Volume) 13 | m.solve(disp=False) 14 | print('width = ' + str(box_width.value[0])) 15 | print('length = ' + str(box_length.value[0])) 16 | print('height = ' + str(box_height.value[0])) 17 | print('volume = ' + str(Volume.value[0])) -------------------------------------------------------------------------------- /Module_02/4_TCLab/TCLabSim_2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.integrate import odeint 4 | import tclab 5 | import time 6 | 7 | n = 300 # Number of second time points (5 min) 8 | tm = np.linspace(0,n,n+1) # Time values 9 | 10 | # data 11 | lab = tclab.TCLab() 12 | T1 = [lab.T1] 13 | lab.Q1(50) 14 | for i in range(n): 15 | time.sleep(1) 16 | print(lab.T1) 17 | T1.append(lab.T1) 18 | lab.close() 19 | 20 | # simulation 21 | def labsim(TC,t): 22 | dTCdt = ((23-TC) + 0.8*50)/120.0 23 | return dTCdt 24 | Tsim = odeint(labsim,23,tm) 25 | 26 | # Plot results 27 | plt.figure(1) 28 | plt.plot(tm,Tsim,'b-',label='Simulated') 29 | plt.plot(tm,T1,'r.',label='Measured') 30 | plt.ylabel('Temperature (degC)') 31 | plt.xlabel('Time (sec)') 32 | plt.legend() 33 | plt.show() -------------------------------------------------------------------------------- /Module_05/1_Topic/ModelLinearization_1.py: -------------------------------------------------------------------------------- 1 | # analytic solution with Python 2 | import sympy as sp 3 | sp.init_printing() 4 | # define symbols 5 | x,u = sp.symbols(['x','u']) 6 | # define equation 7 | dxdt = -x**2 + sp.sqrt(u) 8 | 9 | print(sp.diff(dxdt,x)) 10 | print(sp.diff(dxdt,u)) 11 | 12 | # numeric solution with Python 13 | import numpy as np 14 | from scipy.misc import derivative 15 | u = 16.0 16 | x = 2.0 17 | def pd_x(x): 18 | dxdt = -x**2 + np.sqrt(u) 19 | return dxdt 20 | def pd_u(u): 21 | dxdt = -x**2 + np.sqrt(u) 22 | return dxdt 23 | 24 | print('Approximate Partial Derivatives') 25 | print(derivative(pd_x,x,dx=1e-4)) 26 | print(derivative(pd_u,u,dx=1e-4)) 27 | 28 | print('Exact Partial Derivatives') 29 | print(-2.0*x) # exact d(f(x,u))/dx 30 | print(0.5 / np.sqrt(u)) # exact d(f(x,u))/du -------------------------------------------------------------------------------- /Module_07/3_Assignment/DynamicParameterRegression_1.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import matplotlib.pyplot as plt 3 | 4 | # pipeline data URL (don't need wget) 5 | url = 'https://apmonitor.com/pdc/uploads/Main/pipeline_data.txt' 6 | 7 | # import data with pandas 8 | data = pd.read_csv(url) 9 | time = 'Time (min)' 10 | valve = 'Valve Position (% open)' 11 | TC = 'Temperature (degC)' 12 | 13 | # print temperature values 14 | print(TC) 15 | print(data[TC][0:5]) 16 | print('min: '+str(min(data[TC]))) 17 | print('max: '+str(max(data[TC]))) 18 | 19 | # plot data with pyplot 20 | plt.figure() 21 | plt.subplot(2,1,1) 22 | plt.plot(data[time]/60.0,data[valve],'b--') 23 | plt.ylabel(valve) 24 | 25 | plt.subplot(2,1,2) 26 | plt.plot(data[time]/60.0,data[TC],'r-') 27 | plt.ylabel(TC) 28 | plt.xlabel('Time (hr)') 29 | plt.show() -------------------------------------------------------------------------------- /Module_23/4_TCLab/ArduinoControl2_2.py: -------------------------------------------------------------------------------- 1 | import tclab 2 | import time 3 | import numpy as np 4 | from simple_pid import PID 5 | 6 | # Connect to Arduino 7 | a = tclab.TCLab() 8 | 9 | # Create PID controllers 10 | pid1 = PID(Kp=2,Ki=2/136,Kd=0,\ 11 | setpoint=40,sample_time=1.0,output_limits=(0,100)) 12 | pid2 = PID(Kp=4,Ki=4/136,Kd=0,\ 13 | setpoint=35,sample_time=1.0,output_limits=(0,100)) 14 | 15 | for i in range(600): # 10 minutes (600 sec) 16 | # pid control 17 | OP1 = pid1(a.T1) 18 | OP2 = pid2(a.T2) 19 | a.Q1(OP1) 20 | a.Q2(OP2) 21 | 22 | # print line 23 | print('Heater: ' + str(round(OP1,2)) + '%' + \ 24 | ' T1 PV: ' + str(a.T1) + 'degC' + \ 25 | ' T1 SP: ' + str(pid1.setpoint) + 'degC') 26 | 27 | # wait for next sample time 28 | time.sleep(pid.sample_time) -------------------------------------------------------------------------------- /Module_06/4_TCLab/TCLabFOPDT_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | 6 | n = 480 # Number of second time points (8 min) 7 | tm = np.linspace(0,n,n+1) # Time values 8 | 9 | # data 10 | lab = tclab.TCLab() 11 | T1 = [lab.T1] 12 | Q1 = np.zeros(n+1) 13 | Q1[30:] = 70.0 14 | for i in range(n): 15 | lab.Q1(Q1[i]) 16 | time.sleep(1) 17 | print(lab.T1) 18 | T1.append(lab.T1) 19 | lab.close() 20 | 21 | # Create Figure 22 | plt.figure(figsize=(12,8)) 23 | ax = plt.subplot(2,1,1) 24 | ax.grid() 25 | plt.plot(tm/60.0,T1,'r.',label=r'$T_1$') 26 | plt.ylabel(r'Temp ($^oC$)') 27 | ax = plt.subplot(2,1,2) 28 | ax.grid() 29 | plt.plot(tm/60.0,Q1,'b-',label=r'$Q_1$') 30 | plt.ylabel(r'Heater (%)') 31 | plt.xlabel('Time (min)') 32 | plt.legend() 33 | plt.savefig('Step_Response.png') 34 | plt.show() -------------------------------------------------------------------------------- /Module_02/4_TCLab/TCLabSim_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | # pip install gekko 6 | from gekko import GEKKO 7 | 8 | n = 300 # Number of second time points (5 min) 9 | 10 | # data 11 | lab = tclab.TCLab() 12 | T1 = [lab.T1] 13 | lab.Q1(50) 14 | for i in range(n): 15 | time.sleep(1) 16 | print(lab.T1) 17 | T1.append(lab.T1) 18 | lab.close() 19 | 20 | # simulation 21 | m = GEKKO() 22 | m.time = np.linspace(0,n,n+1) 23 | TC = m.Var(23) 24 | m.Equation(120*TC.dt()==(23-TC)+0.8*50) 25 | m.options.IMODE = 4 # dynamic simulation 26 | m.solve(disp=False) 27 | 28 | # Plot results 29 | plt.figure(1) 30 | plt.plot(m.time,TC,'b-',label='Simulated') 31 | plt.plot(m.time,T1,'r.',label='Measured') 32 | plt.ylabel('Temperature (degC)') 33 | plt.xlabel('Time (sec)') 34 | plt.legend() 35 | plt.show() -------------------------------------------------------------------------------- /Module_38/2_Quiz/QuizNonlinearProgramming_1.py: -------------------------------------------------------------------------------- 1 | from scipy.optimize import minimize 2 | def objective(x): 3 | return x[0]*x[3]*(x[0]+x[1]+x[2])+x[2] 4 | def constraint1(x): 5 | return x[0]*x[1]*x[2]*x[3]-25.0 6 | def constraint2(x): 7 | return 40.0 - sum([xi**2 for xi in x]) 8 | x0 = [1,5,5,1] # initial guesses 9 | 10 | print('Initial SSE Objective: ' + str(objective(x0))) 11 | 12 | # optimize 13 | b = (1.0,5.0) 14 | bnds = (b, b, b, b) 15 | con1 = {'type': 'ineq', 'fun': constraint1} 16 | con2 = {'type': 'eq', 'fun': constraint2} 17 | cons = ([con1,con2]) 18 | solution = minimize(objective,x0,method='SLSQP',\ 19 | bounds=bnds,constraints=cons) 20 | x = solution.x 21 | print('Final SSE Objective: ' + str(objective(x))) 22 | print('x1 = ' + str(x[0])); print('x2 = ' + str(x[1])) 23 | print('x3 = ' + str(x[2])); print('x4 = ' + str(x[3])) -------------------------------------------------------------------------------- /Module_38/2_Quiz/QuizNonlinearProgramming_2.py: -------------------------------------------------------------------------------- 1 | from scipy.optimize import minimize 2 | def objective(x): 3 | return x[0]*x[3]*(x[0]+x[1]+x[2])+x[2] 4 | def constraint1(x): 5 | return x[0]*x[1]*x[2]*x[3]-25.0 6 | def constraint2(x): 7 | return 40.0 - sum([xi**2 for xi in x]) 8 | x0 = [1,5,5,1] # initial guesses 9 | 10 | print('Initial SSE Objective: ' + str(objective(x0))) 11 | 12 | # optimize 13 | b = (1.0,5.0) 14 | bnds = ((1.4,5.0), b, b, b) 15 | con1 = {'type': 'ineq', 'fun': constraint1} 16 | con2 = {'type': 'eq', 'fun': constraint2} 17 | cons = ([con1,con2]) 18 | solution = minimize(objective,x0,method='SLSQP',\ 19 | bounds=bnds,constraints=cons) 20 | x = solution.x 21 | print('Final SSE Objective: ' + str(objective(x))) 22 | print('x1 = ' + str(x[0])); print('x2 = ' + str(x[1])) 23 | print('x3 = ' + str(x[2])); print('x4 = ' + str(x[3])) -------------------------------------------------------------------------------- /Module_16/4_TCLab/TCLabPIDFeedforward_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | 6 | n = 600 # Number of second time points (10 min) 7 | tm = np.linspace(0,n,n+1) # Time values 8 | 9 | # data 10 | lab = tclab.TCLab() 11 | T1 = [lab.T1] 12 | T2 = [lab.T2] 13 | lab.Q2(100) 14 | for i in range(n): 15 | time.sleep(1) 16 | print(lab.T1,lab.T2) 17 | T1.append(lab.T1) 18 | T2.append(lab.T2) 19 | lab.close() 20 | 21 | # Disturbance Gain 22 | Kd = (T1[-1]-T1[0]) / (T2[-1]-T2[0]) 23 | 24 | # Plot results 25 | plt.figure(1) 26 | plt.plot(tm/60.0,T1,'r.',label=r'Measured $T_1$') 27 | plt.plot(tm/60.0,T2,'b.',label=r'Measured $T_2$') 28 | plt.text(3,40,'Disturbance Gain (Kd): '+str(round(Kd,2))) 29 | plt.ylabel(r'Temperature ($^o$C)') 30 | plt.xlabel('Time (min)') 31 | plt.legend() 32 | plt.savefig('Disturbance_gain.png') 33 | plt.show() -------------------------------------------------------------------------------- /Module_06/1_Topic/README.md: -------------------------------------------------------------------------------- 1 | ### [First Order Plus Dead Time (FOPDT)](https://www.apmonitor.com/pdc/index.php/Main/FirstOrderSystems) 2 | - Scripts Located: 0 3 | - Page Description: A first-order linear system with time delay is a common empirical description of many dynamic processes. 4 | ### [Time Delay in Dynamic Systems](https://www.apmonitor.com/pdc/index.php/Main/FirstOrderPlusDeadTime) 5 | - Scripts Located: 1 6 | - Page Description: Time delay in a first-order linear system is a shift in the effect of an input on the output response. 7 | ### [Graphical Method: FOPDT to Step Test](https://www.apmonitor.com/pdc/index.php/Main/FirstOrderGraphical) 8 | - Scripts Located: 2 9 | - Page Description: A first-order linear system with time delay is a common empirical description of many dynamic processes. This Python source code demonstrates how to simulate a step test and compare with an FOPDT approximation. 10 | -------------------------------------------------------------------------------- /Module_13/4_TCLab/TCLabPIDControl_1.py: -------------------------------------------------------------------------------- 1 | def pid(sp,pv,pv_last,ierr,dt): 2 | # Parameters in terms of PID coefficients 3 | KP = Kc 4 | KI = Kc/tauI 5 | KD = Kc*tauD 6 | # ubias for controller (initial heater) 7 | op0 = 0 8 | # upper and lower bounds on heater level 9 | ophi = 100 10 | oplo = 0 11 | # calculate the error 12 | error = sp-pv 13 | # calculate the integral error 14 | ierr = ierr + KI * error * dt 15 | # calculate the measurement derivative 16 | dpv = (pv - pv_last) / dt 17 | # calculate the PID output 18 | P = KP * error 19 | I = ierr 20 | D = -KD * dpv 21 | op = op0 + P + I + D 22 | # implement anti-reset windup 23 | if op < oplo or op > ophi: 24 | I = I - KI * error * dt 25 | # clip output 26 | op = max(oplo,min(ophi,op)) 27 | # return the controller output and PID terms 28 | return [op,P,I,D] -------------------------------------------------------------------------------- /Module_03/4_TCLab/TCLabConvection_2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.integrate import odeint 4 | import tclab 5 | import time 6 | 7 | n = 300 # Number of second time points (5 min) 8 | tm = np.linspace(0,n,n+1) # Time values 9 | 10 | # data 11 | lab = tclab.TCLab() 12 | T1 = [lab.T1] 13 | lab.Q1(50) 14 | for i in range(n): 15 | time.sleep(1.0) 16 | print(lab.T1) 17 | T1.append(lab.T1) 18 | lab.close() 19 | 20 | # simulation 21 | def labsim(TC,t): 22 | U = 10.0 23 | A = 0.0012 24 | Cp = 500 25 | m = 0.004 26 | alpha = 0.01 27 | Ta = 23 28 | dTCdt = (U*A*(Ta-TC) + alpha*50)/(m*Cp) 29 | return dTCdt 30 | Tsim = odeint(labsim,23,tm) 31 | 32 | # Plot results 33 | plt.figure(1) 34 | plt.plot(tm,Tsim,'b-',label='Simulated') 35 | plt.plot(tm,T1,'r.',label='Measured') 36 | plt.ylabel('Temperature (degC)') 37 | plt.xlabel('Time (sec)') 38 | plt.legend() 39 | plt.show() -------------------------------------------------------------------------------- /Module_03/4_TCLab/TCLabConvection_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | # pip install gekko 6 | from gekko import GEKKO 7 | 8 | n = 300 # Number of second time points (5 min) 9 | 10 | # data 11 | lab = tclab.TCLab() 12 | T1 = [lab.T1] 13 | lab.Q1(50) 14 | for i in range(n): 15 | time.sleep(1) 16 | print(lab.T1) 17 | T1.append(lab.T1) 18 | lab.close() 19 | 20 | # simulation 21 | m = GEKKO() 22 | m.time = np.linspace(0,n,n+1) 23 | U = 10.0; A = 0.0012; Cp = 500 24 | mass = 0.004; alpha = 0.01; Ta = 23 25 | TC = m.Var(23) 26 | m.Equation(mass*Cp*TC.dt()==U*A*(Ta-TC)+alpha*50) 27 | m.options.IMODE = 4 # dynamic simulation 28 | m.solve(disp=False) 29 | 30 | # Plot results 31 | plt.figure(1) 32 | plt.plot(m.time,TC,'b-',label='Simulated') 33 | plt.plot(m.time,T1,'r.',label='Measured') 34 | plt.ylabel('Temperature (degC)') 35 | plt.xlabel('Time (sec)') 36 | plt.legend() 37 | plt.show() -------------------------------------------------------------------------------- /Module_25/1_Topic/TransferFunctions_2.py: -------------------------------------------------------------------------------- 1 | from gekko import GEKKO 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | # Create GEKKO model 6 | m = GEKKO() 7 | 8 | # Time points for simulation 9 | nt = 81 10 | m.time = np.linspace(0,8,nt) 11 | 12 | # Define input 13 | # First step (up) starts at 1 sec 14 | # Ramp (down) starts at 3 sec 15 | # Ramp completes at 5 sec 16 | ut = np.zeros(nt) 17 | ut[11:31] = 2.0 18 | for i in range(31,51): 19 | ut[i] = ut[i-1] - 0.1 20 | 21 | # Define model 22 | u = m.Param(value=ut) 23 | ud = m.Var() 24 | y = m.Var() 25 | dydt = m.Var() 26 | m.Equation(ud==u) 27 | m.Equation(dydt==y.dt()) 28 | m.Equation(dydt.dt() + 6*y.dt() + 9*y==5*ud.dt()+5*u) 29 | 30 | # Simulation options 31 | m.options.IMODE=7 32 | m.options.NODES=4 33 | m.solve(disp=False) 34 | 35 | # plot results 36 | plt.figure() 37 | plt.plot(m.time,u.value,label='u(t)') 38 | plt.plot(m.time,y.value,label='y(t)') 39 | plt.legend() 40 | plt.xlabel('Time') 41 | plt.show() -------------------------------------------------------------------------------- /Module_17/4_TCLab/TCLabActuator_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | 6 | n = 300 # Number of second time points (5 min) 7 | tm = np.linspace(0,n,n+1) # Time values 8 | 9 | Kp2 = 10 | taup2 = 11 | thetap2 = 12 | Tss = 23.0 13 | Qstep = 65 14 | 15 | # step response simulation 16 | T2s = Kp2 * (1-np.exp(-(tm-thetap2)/taup2)) * Qstep 17 | # time delay 18 | step = np.zeros(n+1) 19 | step[int(np.floor(thetap2)):]=1.0 20 | T2s = T2s * step + Tss 21 | 22 | # step response data 23 | lab = tclab.TCLab() 24 | T2 = [lab.T2] 25 | lab.Q2(Qstep) 26 | for i in range(n): 27 | time.sleep(1) 28 | print(i,lab.T2) 29 | T2.append(lab.T2) 30 | lab.close() 31 | 32 | # Plot results 33 | plt.figure(figsize=(8,5)) 34 | plt.plot(tm,T2,'r.',label='Measured') 35 | plt.plot(tm,T2s,'k-',label='Predicted') 36 | plt.ylabel('Temperature (degC)') 37 | plt.xlabel('Time (sec)') 38 | plt.legend() 39 | plt.savefig('Q2_step_test.png') 40 | plt.show() -------------------------------------------------------------------------------- /Module_29/1_Topic/ModelSimulation_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy import signal 3 | import matplotlib.pyplot as plt 4 | from scipy.integrate import odeint 5 | 6 | # Simulate taup * dy/dt = -y + K*u 7 | Kp = 3.0 8 | taup = 2.0 9 | 10 | # (1) Transfer Function 11 | num = [Kp] 12 | den = [taup,1] 13 | sys1 = signal.TransferFunction(num,den) 14 | t1,y1 = signal.step(sys1) 15 | 16 | # (2) State Space 17 | A = -1.0/taup 18 | B = Kp/taup 19 | C = 1.0 20 | D = 0.0 21 | sys2 = signal.StateSpace(A,B,C,D) 22 | t2,y2 = signal.step(sys2) 23 | 24 | # (3) ODE Integrator 25 | def model3(y,t): 26 | u = 1 27 | return (-y + Kp * u)/taup 28 | t3 = np.linspace(0,14,100) 29 | y3 = odeint(model3,0,t3) 30 | 31 | plt.figure(1) 32 | plt.plot(t1,y1,'b--',linewidth=3,label='Transfer Fcn') 33 | plt.plot(t2,y2,'g:',linewidth=2,label='State Space') 34 | plt.plot(t3,y3,'r-',linewidth=1,label='ODE Integrator') 35 | plt.xlabel('Time') 36 | plt.ylabel('Response (y)') 37 | plt.legend(loc='best') 38 | plt.show() -------------------------------------------------------------------------------- /Module_05/1_Topic/ModelLinearization_2.py: -------------------------------------------------------------------------------- 1 | from mpl_toolkits.mplot3d import Axes3D 2 | import matplotlib.pyplot as plt 3 | from matplotlib import cm 4 | from matplotlib.ticker import LinearLocator, FormatStrFormatter 5 | import numpy as np 6 | 7 | fig = plt.figure() 8 | ax = fig.gca(projection='3d') 9 | 10 | # Make data. 11 | X = np.arange(0, 4, 0.25) 12 | U = np.arange(0, 20, 0.25) 13 | X, U = np.meshgrid(X, U) 14 | DXDT = -X**2 + np.sqrt(U) 15 | LIN = -4.0 * (X-2.0) + 1.0/8.0 * (U-16.0) 16 | 17 | # Plot the surface. 18 | surf = ax.plot_wireframe(X, U, LIN) 19 | surf = ax.plot_surface(X, U, DXDT, cmap=cm.coolwarm, 20 | linewidth=0, antialiased=False) 21 | 22 | # Customize the z axis. 23 | ax.set_zlim(-10.0, 5.0) 24 | ax.zaxis.set_major_locator(LinearLocator(10)) 25 | ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f')) 26 | 27 | # Add a color bar which maps values to colors. 28 | fig.colorbar(surf, shrink=0.5, aspect=5) 29 | 30 | # Add labels 31 | plt.xlabel('x') 32 | plt.ylabel('u') 33 | 34 | plt.show() -------------------------------------------------------------------------------- /Module_16/README.md: -------------------------------------------------------------------------------- 1 | ## Module 16 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Quiz 5 | - [Quiz on Artificial Pancreas Case Study](https://www.apmonitor.com/pdc/index.php/Main/QuizDiabetesControl): 0 scripts. Description: Learning assessment on case study of an artificial pancreas control development. 6 | ### Assignment 7 | - [Maintain Glucose in Type-I Diabetic](https://www.apmonitor.com/pdc/index.php/Main/DiabeticBloodGlucose): 2 scripts. Description: Case study on control of glucose level in a Type-I diabetic with a PID controller in Python. The exercise involves creating a dynamic first-order model, obtaining tuning parameters, and tuning the controller. 8 | ### TCLab 9 | - [TCLab PID with Feedforward](https://www.apmonitor.com/pdc/index.php/Main/TCLabPIDFeedforward): 5 scripts. Description: TCLab with proportional integral derivative (PID) control tuning and feedforward trim for disturbance rejection. 10 | -------------------------------------------------------------------------------- /Module_14/README.md: -------------------------------------------------------------------------------- 1 | ## Module 14 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Quiz 5 | - [Quiz on Level Control Case Study](https://www.apmonitor.com/pdc/index.php/Main/QuizLevelControl): 0 scripts. Description: Learning assessment on level control with a PID controller. 6 | ### Assignment 7 | - [Automation of Liquid Level Control](https://www.apmonitor.com/pdc/index.php/Main/LevelControl): 3 scripts. Description: Case study on a liquid level in a dual gravity drained tank with a PI or PID controller in Python. The exercise involves creating a dynamic first-order model, obtaining tuning parameters, and tuning the controller. 8 | ### TCLab 9 | - [TCLab PI Control Tuning](https://www.apmonitor.com/pdc/index.php/Main/TCLabPIControlTuning): 6 scripts. Description: TCLab with proportional integral (PI) control eliminates offset between the setpoint and measured temperature. Use IMC and ITAE tuning and compare the response 10 | -------------------------------------------------------------------------------- /Module_24/1_Topic/LaplaceTransforms_1.py: -------------------------------------------------------------------------------- 1 | import sympy as sym 2 | from sympy.abc import s,t,x,y,z 3 | from sympy.integrals import laplace_transform 4 | from sympy.integrals import inverse_laplace_transform 5 | 6 | # Laplace transform (t->s) 7 | U = laplace_transform(5*t, t, s) 8 | print('U') 9 | print(U[0]) 10 | # Result: 5/s**2 11 | 12 | # Inverse Laplace transform (s->t) 13 | X = inverse_laplace_transform(U[0],s,t) 14 | print('X') 15 | print(X) 16 | # Result: 5*t*Heaviside(t) 17 | 18 | # Function 19 | F = 5*(s+1)/(s+3)**2 20 | print('F') 21 | print(F) 22 | # Result: (5*s + 5)/(s + 3)**2 23 | 24 | # Partial fraction decomposition 25 | G = sym.apart(F) 26 | print('G') 27 | print(G) 28 | # Result: 5/(s + 3) - 10/(s + 3)**2 29 | 30 | # denominator of transfer function 31 | d1 = (s+1)*(s+3)*(s**2+3*s+1) 32 | 33 | # expand polynomial 34 | d2 = sym.expand(d1) 35 | print('d2') 36 | print(d2) 37 | # Result: s**4 + 7*s**3 + 16*s**2 + 13*s + 3 38 | 39 | # find roots 40 | print(sym.roots(d2)) 41 | # Result: {-1: 1, -3: 1, -3/2 - sqrt(5)/2: 1, -3/2 + sqrt(5)/2: 1} -------------------------------------------------------------------------------- /Module_03/README.md: -------------------------------------------------------------------------------- 1 | ## Module 3 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Dynamic Model Introduction](https://www.apmonitor.com/pdc/index.php/Main/DynamicModeling): 1 scripts. Description: Introduction to dynamic modeling and a course overview 6 | ### Quiz 7 | - [Quiz: Physics-based Modeling](https://www.apmonitor.com/pdc/index.php/Main/QuizBalanceEquations): 0 scripts. Description: Learning assessment on physics-based modeling methods with an introduction to conserved quantities such as mass, energy, and momentum. 8 | ### Assignment 9 | - [Derive Transient Balance Equations](https://www.apmonitor.com/pdc/index.php/Main/DeriveBalanceEquations): 0 scripts. Description: The exercise involves deriving a transient model from balance equations. 10 | ### TCLab 11 | - [TCLab Convective Heat Transfer](https://www.apmonitor.com/pdc/index.php/Main/TCLabConvection): 3 scripts. Description: Convective Heat Transfer Prediction with a Transistor Heater 12 | -------------------------------------------------------------------------------- /Module_30/3_Assignment/StabilityAnalysisExercises_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy import signal 3 | import matplotlib.pyplot as plt 4 | 5 | name = 'Problem 1A' 6 | num = [4.0] 7 | p1 = np.poly1d([1,-1]) 8 | p2 = np.poly1d([1,2]) 9 | p3 = np.poly1d([1,3]) 10 | den = p1*p2*p3 11 | sys = signal.TransferFunction(num,den) 12 | t,y = signal.step(sys) 13 | plt.figure(1) 14 | plt.plot(t,y,'k-',label=name) 15 | plt.legend(loc='best') 16 | 17 | name = 'Problem 1B' 18 | num = [1,-1,2] 19 | p1 = [1,1] 20 | p2 = [1,1,1] 21 | den = p1 22 | t = np.linspace(0,50,1000) 23 | for i in range(9): 24 | den = np.convolve(den,p1) 25 | den = np.convolve(den,p2) 26 | print(den) 27 | sys = signal.TransferFunction(num,den) 28 | t,y = signal.step(sys,T=t) 29 | plt.figure(2) 30 | plt.plot(t,y,'k-',label=name) 31 | plt.legend(loc='best') 32 | 33 | name = 'Problem 1C' 34 | num = [1] 35 | p1 = np.poly1d([1,1]) 36 | p2 = np.poly1d([1,1,1]) 37 | den = p1*p2 38 | print(den) 39 | sys = signal.TransferFunction(num,den) 40 | t,y = signal.step(sys,T=t) 41 | plt.figure(3) 42 | plt.plot(t,y,'k-',label=name) 43 | plt.legend(loc='best') 44 | plt.show() -------------------------------------------------------------------------------- /Module_15/README.md: -------------------------------------------------------------------------------- 1 | ## Module 15 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Quiz 5 | - [Quiz on CSTR Case Study](https://www.apmonitor.com/pdc/index.php/Main/QuizReactorControl): 0 scripts. Description: Learning assessment on temperature control of a continuously stirred tank reactor (CSTR). 6 | ### Assignment 7 | - [Temperature Control of a Stirred Reactor](https://www.apmonitor.com/pdc/index.php/Main/StirredReactor): 2 scripts. Description: Case study on temperature control in a stirred tank reactor with a PI or PID controller in Python. The exercise involves creating a dynamic first-order model, obtaining tuning parameters, and tuning the controller. 8 | ### TCLab 9 | - [TCLab PID Control Tuning](https://www.apmonitor.com/pdc/index.php/Main/TCLabPIDControlTuning): 3 scripts. Description: TCLab with proportional integral derivative (PID) control tuning. Tuning a PID controller is the process of finding parameters that improve the controller response to disturbances and setpoint changes. 10 | -------------------------------------------------------------------------------- /Module_27/4_TCLab/TCLabOnOffControl_3.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import pandas as pd 3 | import numpy as np 4 | 5 | data = pd.read_csv('data.csv') 6 | 7 | # graphical fit 8 | Delta_SP = 20 9 | Delta_T1 = 21 10 | OS = (44-41)/(41-20) 11 | tp = 86.0 12 | Kp = Delta_T1/Delta_SP 13 | lnOS2 = (np.log(OS))**2 14 | zeta = np.sqrt(lnOS2/(np.pi**2+lnOS2)) 15 | taus = tp * np.sqrt(1-zeta**2)/np.pi 16 | print('Kp: ' + str(Kp)) 17 | print('zeta: ' + str(zeta)) 18 | print('taus: ' + str(taus)) 19 | 20 | # analytic solution 21 | t = data['Time'].values 22 | T0 = data['T1'].values[0] 23 | a = np.sqrt(1-zeta**2) 24 | b = t/taus 25 | c = np.cos(a*b) 26 | d = (zeta/a)*np.sin(a*b) 27 | T1 = Kp*Delta_SP*(1-np.exp(-zeta*b)*(c+d))+T0 28 | 29 | plt.figure(figsize=(10,7)) 30 | ax=plt.subplot(2,1,1); ax.grid() 31 | plt.plot(data['Time'],data['Q1'],'b-',label=r'$Q_1$ (%)') 32 | plt.legend(); plt.ylabel('Heater') 33 | ax=plt.subplot(2,1,2); ax.grid() 34 | plt.plot(data['Time'],data['T1'],'r-',label=r'$T_1$ Meas $(^oC)$') 35 | plt.plot(t,T1,'k:',label=r'$T_1$ Pred $(^oC)$') 36 | plt.legend(); plt.xlabel('Time (sec)'); plt.ylabel('Temperature') 37 | plt.show() -------------------------------------------------------------------------------- /Module_21/4_TCLab/ArduinoModeling2_3.py: -------------------------------------------------------------------------------- 1 | % save as heat2.m 2 | % define energy balance model 3 | function dTdt = heat2(t,x,Q1,Q2) 4 | % Parameters 5 | Ta = 23 + 273.15; % K 6 | U = 10.0; % W/m^2-K 7 | m = 4.0/1000.0; % kg 8 | Cp = 0.5 * 1000.0; % J/kg-K 9 | A = 10.0 / 100.0^2; % Area in m^2 10 | As = 2.0 / 100.0^2; % Area in m^2 11 | alpha1 = 0.0100; % W / % heater 1 12 | alpha2 = 0.0075; % W / % heater 2 13 | eps = 0.9; % Emissivity 14 | sigma = 5.67e-8; % Stefan-Boltzman 15 | 16 | % Temperature States 17 | T1 = x(1); 18 | T2 = x(2); 19 | 20 | % Heat Transfer Exchange Between 1 and 2 21 | conv12 = U*As*(T2-T1); 22 | rad12 = eps*sigma*As * (T2^4 - T1^4); 23 | 24 | % Nonlinear Energy Balances 25 | dT1dt = (1.0/(m*Cp))*(U*A*(Ta-T1) ... 26 | + eps * sigma * A * (Ta^4 - T1^4) ... 27 | + conv12 + rad12 ... 28 | + alpha1*Q1); 29 | dT2dt = (1.0/(m*Cp))*(U*A*(Ta-T2) ... 30 | + eps * sigma * A * (Ta^4 - T2^4) ... 31 | - conv12 - rad12 ... 32 | + alpha2*Q2); 33 | dTdt = [dT1dt,dT2dt]'; 34 | end -------------------------------------------------------------------------------- /Module_38/1_Topic/NonlinearProgramming_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.optimize import minimize 3 | 4 | def objective(x): 5 | return x[0]*x[3]*(x[0]+x[1]+x[2])+x[2] 6 | 7 | def constraint1(x): 8 | return x[0]*x[1]*x[2]*x[3]-25.0 9 | 10 | def constraint2(x): 11 | sum_eq = 40.0 12 | for i in range(4): 13 | sum_eq = sum_eq - x[i]**2 14 | return sum_eq 15 | 16 | # initial guesses 17 | n = 4 18 | x0 = np.zeros(n) 19 | x0[0] = 1.0 20 | x0[1] = 5.0 21 | x0[2] = 5.0 22 | x0[3] = 1.0 23 | 24 | # show initial objective 25 | print('Initial SSE Objective: ' + str(objective(x0))) 26 | 27 | # optimize 28 | b = (1.0,5.0) 29 | bnds = (b, b, b, b) 30 | con1 = {'type': 'ineq', 'fun': constraint1} 31 | con2 = {'type': 'eq', 'fun': constraint2} 32 | cons = ([con1,con2]) 33 | solution = minimize(objective,x0,method='SLSQP',\ 34 | bounds=bnds,constraints=cons) 35 | x = solution.x 36 | 37 | # show final objective 38 | print('Final SSE Objective: ' + str(objective(x))) 39 | 40 | # print solution 41 | print('Solution') 42 | print('x1 = ' + str(x[0])) 43 | print('x2 = ' + str(x[1])) 44 | print('x3 = ' + str(x[2])) 45 | print('x4 = ' + str(x[3])) -------------------------------------------------------------------------------- /Module_04/README.md: -------------------------------------------------------------------------------- 1 | ## Module 4 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Balance Equations](https://www.apmonitor.com/pdc/index.php/Main/PhysicsBasedModels): 1 scripts. Description: Balance equations are the foundation of many physics-based models. Mass, energy, momentum, and species balances are from conservation laws. 6 | ### Quiz 7 | - [Quiz: Transient Balance Equations](https://www.apmonitor.com/pdc/index.php/Main/QuizTransientBalanceEquations): 1 scripts. Description: Learning assessment on transient balance equations with an introduction to conserved quantities such as mass, energy, and momentum. 8 | ### Assignment 9 | - [Tank Blending](https://www.apmonitor.com/pdc/index.php/Main/TankBlending): 1 scripts. Description: Case study on modeling of a tank mixer. The exercise involves creating a dynamic model and simulating it in Python. 10 | ### TCLab 11 | - [TCLab Radiative Heat Transfer](https://www.apmonitor.com/pdc/index.php/Main/TCLabRadiative): 3 scripts. Description: Radiative and Convective Heat Transfer Prediction with a Transistor Heater 12 | -------------------------------------------------------------------------------- /Module_21/1_Topic/ArduinoModeling_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.integrate import odeint 4 | 5 | # define energy balance model 6 | def heat(x,t,Q): 7 | # Parameters 8 | Ta = 23 + 273.15 # K 9 | U = 10.0 # W/m^2-K 10 | m = 4.0/1000.0 # kg 11 | Cp = 0.5 * 1000.0 # J/kg-K 12 | A = 12.0 / 100.0**2 # Area in m^2 13 | alpha = 0.01 # W / % heater 14 | eps = 0.9 # Emissivity 15 | sigma = 5.67e-8 # Stefan-Boltzman 16 | 17 | # Temperature State 18 | T = x[0] 19 | 20 | # Nonlinear Energy Balance 21 | dTdt = (1.0/(m*Cp))*(U*A*(Ta-T) \ 22 | + eps * sigma * A * (Ta**4 - T**4) \ 23 | + alpha*Q) 24 | return dTdt 25 | 26 | Q = 100.0 # Percent Heater (0-100%) 27 | T0 = 23.0 + 273.15 # Initial temperature 28 | n = 60*10+1 # Number of second time points (10min) 29 | time = np.linspace(0,n-1,n) # Time vector 30 | T = odeint(heat,300.0,time,args=(Q,)) # Integrate ODE 31 | 32 | # Plot results 33 | plt.figure(1) 34 | plt.plot(time/60.0,T-273.15,'b-') 35 | plt.ylabel('Temperature (degC)') 36 | plt.xlabel('Time (min)') 37 | plt.legend(['Step Test (0-100% heater)']) 38 | plt.show() -------------------------------------------------------------------------------- /Module_22/4_TCLab/ArduinoEstimation2_9.py: -------------------------------------------------------------------------------- 1 | % define energy balance model 2 | function dTdt = heat(t,x,Q1,Q2,p) 3 | %U = 10.0; % W/m^2-K 4 | %alpha1 = 0.0100; % W / % heater 1 5 | %alpha2 = 0.0075; % W / % heater 2 6 | U = p(1); 7 | alpha1 = p(2); 8 | alpha2 = p(3); 9 | 10 | % Parameters 11 | Ta = 23 + 273.15; % K 12 | m = 4.0/1000.0; % kg 13 | Cp = 0.5 * 1000.0; % J/kg-K 14 | A = 10.0 / 100.0^2; % Area in m^2 15 | As = 2.0 / 100.0^2; % Area in m^2 16 | eps = 0.9; % Emissivity 17 | sigma = 5.67e-8; % Stefan-Boltzman 18 | 19 | % Temperature States 20 | T1 = x(1)+273.15; 21 | T2 = x(2)+273.15; 22 | 23 | % Heat Transfer Exchange Between 1 and 2 24 | conv12 = U*As*(T2-T1); 25 | rad12 = eps*sigma*As * (T2^4 - T1^4); 26 | 27 | % Nonlinear Energy Balances 28 | dT1dt = (1.0/(m*Cp))*(U*A*(Ta-T1) ... 29 | + eps * sigma * A * (Ta^4 - T1^4) ... 30 | + conv12 + rad12 ... 31 | + alpha1*Q1); 32 | dT2dt = (1.0/(m*Cp))*(U*A*(Ta-T2) ... 33 | + eps * sigma * A * (Ta^4 - T2^4) ... 34 | - conv12 - rad12 ... 35 | + alpha2*Q2); 36 | dTdt = [dT1dt,dT2dt]'; 37 | end -------------------------------------------------------------------------------- /Module_28/README.md: -------------------------------------------------------------------------------- 1 | ## Module 28 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Optimization Method: SOPDT to Data](https://www.apmonitor.com/pdc/index.php/Main/SecondOrderOptimization): 2 scripts. Description: A second-order linear system with time delay is a common empirical description of many dynamic processes. This tutorial demonstrates how to fit an SOPDT model to data with optimization techniques. 6 | ### Quiz 7 | - [Quiz on Second Order Regression](https://www.apmonitor.com/pdc/index.php/Main/QuizSecondOrderOptimization): 4 scripts. Description: Learning assessment on second order dynamic system regression methods. 8 | ### Assignment 9 | - [Fit Second Order with Optimization](https://www.apmonitor.com/pdc/index.php/Main/SecondOrderOptimizationFit): 2 scripts. Description: Optimization methods are used to fit unknown parameters for a second order system. 10 | ### TCLab 11 | - [TCLab Second Order Regression](https://www.apmonitor.com/pdc/index.php/Main/TCLabSecondOrder): 4 scripts. Description: A 2nd order model is fit to an open-loop and closed-loop temperature response to heater changes. 12 | -------------------------------------------------------------------------------- /Module_10/README.md: -------------------------------------------------------------------------------- 1 | ## Module 10 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Process Control Introduction](https://www.apmonitor.com/pdc/index.php/Main/FeedbackControl): 0 scripts. Description: Introduction to process controllers with examples of actuators, sensors, and controllers for engineers. 6 | ### Quiz 7 | - [Quiz on Controller Design](https://www.apmonitor.com/pdc/index.php/Main/QuizControlDesign): 0 scripts. Description: Learning assessment on controller design. Controller design is the planning of sensors, actuators, and algorithms to automatically regulate a process. 8 | ### Assignment 9 | - [Process Controller Design Exercises](https://www.apmonitor.com/pdc/index.php/Main/ControllerDesign): 0 scripts. Description: Introduction to process controllers with examples of actuators, sensors, and controllers for engineers. 10 | ### TCLab 11 | - [TCLab Controller Design](https://www.apmonitor.com/pdc/index.php/Main/TCLabControlDesign): 0 scripts. Description: Design a controller for automation of temperature regulation to a setpoint. The controller adjusts a heater to regulate the temperature. 12 | -------------------------------------------------------------------------------- /Module_13/README.md: -------------------------------------------------------------------------------- 1 | ## Module 13 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Proportional Integral Derivative (PID)](https://www.apmonitor.com/pdc/index.php/Main/ProportionalIntegralDerivative): 1 scripts. Description: Proportional Integral Derivative (PID) control is the most commonly used controller in practice. This demonstrates how to obtain tuning values for a PID controller from step test data. 6 | ### Quiz 7 | - [Quiz on PID Control](https://www.apmonitor.com/pdc/index.php/Main/QuizProportionalIntegralDerivative): 0 scripts. Description: Learning assessment on Proportional Integral Derivative (PID) Control. 8 | ### Assignment 9 | - [Concentration PID Control](https://www.apmonitor.com/pdc/index.php/Main/ControlBlending): 1 scripts. Description: Case study on controlling tank mixer concentration. The exercise involves using a dynamic model and adding a PI controller in Python. 10 | ### TCLab 11 | - [TCLab PID Control](https://www.apmonitor.com/pdc/index.php/Main/TCLabPIDControl): 3 scripts. Description: TCLab with proportional integral derivative (PID) control. Use IMC and ITAE tuning and compare the response 12 | -------------------------------------------------------------------------------- /Module_12/README.md: -------------------------------------------------------------------------------- 1 | ## Module 12 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Proportional Integral (PI) Control](https://www.apmonitor.com/pdc/index.php/Main/ProportionalIntegralControl): 1 scripts. Description: Proportional Integral (PI) control is a common variant of PID control that does not have a derivative term. It is used to remove offset that is commonly found with P-only controllers. 6 | ### Quiz 7 | - [Quiz on Proportional Integral (PI) Control](https://www.apmonitor.com/pdc/index.php/Main/QuizProportionalIntegralControl): 1 scripts. Description: Learning assessment on proportional integral (PI) control as the most common variant of Proportional Integral Derivative (PID) Control. 8 | ### Assignment 9 | - [Automobile Velocity Control](https://www.apmonitor.com/pdc/index.php/Main/SpeedControl): 1 scripts. Description: Case study on automobile velocity control with a PI controller in Python 10 | ### TCLab 11 | - [TCLab PI Control](https://www.apmonitor.com/pdc/index.php/Main/TCLabPIControl): 2 scripts. Description: TCLab with proportional integral (PI) control eliminates offset between the setpoint and measured temperature 12 | -------------------------------------------------------------------------------- /Module_29/README.md: -------------------------------------------------------------------------------- 1 | ## Module 29 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Dynamic Simulation in Python](https://www.apmonitor.com/pdc/index.php/Main/ModelSimulation): 2 scripts. Description: Three methods to represent differential equations are (1) transfer functions, (2) state space, and (3) semi-explicit differential equation forms. Python is used to simulate a step response in these three forms. 6 | ### Quiz 7 | - [Quiz on Higher Order Simulation](https://www.apmonitor.com/pdc/index.php/Main/QuizModelSimulation): 0 scripts. Description: Learning assessment on higher order dynamic system simulation. 8 | ### Assignment 9 | - [Distillate Composition Control](https://www.apmonitor.com/pdc/index.php/Main/DistillationControl): 3 scripts. Description: Maintain an overhead mole fraction for the light component in a binary distillation column that separates cyclohexane and n-heptane. 10 | ### TCLab 11 | - [TCLab Higher Order Models](https://www.apmonitor.com/pdc/index.php/Main/TCLabHigherOrder): 5 scripts. Description: Derive and simulate a transfer function, state space, and time series model of T2 temperature response to heater Q1 12 | -------------------------------------------------------------------------------- /Module_30/README.md: -------------------------------------------------------------------------------- 1 | ## Module 30 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Controller Stability Analysis](https://www.apmonitor.com/pdc/index.php/Main/StabilityAnalysis): 2 scripts. Description: Stability analysis is finding the range of controller gains that lead to a stabilizing controller. There are multiple methods to compute this range. 6 | ### Quiz 7 | - [Quiz on Stability Analysis](https://www.apmonitor.com/pdc/index.php/Main/QuizStabilityAnalysis): 0 scripts. Description: Learning assessment on stability analysis. 8 | ### Assignment 9 | - [Stability Analysis](https://www.apmonitor.com/pdc/index.php/Main/StabilityAnalysisExercises): 4 scripts. Description: Stability analysis is finding the range of controller gains that lead to a stabilizing controller. There are multiple methods to compute this range. 10 | ### TCLab 11 | - [TCLab Stability Analysis](https://www.apmonitor.com/pdc/index.php/Main/TCLabStabilityAnalysis): 3 scripts. Description: Stability analysis for the Temperature Control Lab is finding the range of controller gains that lead to a stabilizing controller. There are multiple methods to compute this range. 12 | -------------------------------------------------------------------------------- /Module_02/1_Topic/SolveDifferentialEquations_6.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.integrate import odeint 3 | import matplotlib.pyplot as plt 4 | 5 | # function that returns dz/dt 6 | def model(z,t,u): 7 | x = z[0] 8 | y = z[1] 9 | dxdt = (-x + u)/2.0 10 | dydt = (-y + x)/5.0 11 | dzdt = [dxdt,dydt] 12 | return dzdt 13 | 14 | # initial condition 15 | z0 = [0,0] 16 | 17 | # number of time points 18 | n = 401 19 | 20 | # time points 21 | t = np.linspace(0,40,n) 22 | 23 | # step input 24 | u = np.zeros(n) 25 | # change to 2.0 at time = 5.0 26 | u[51:] = 2.0 27 | 28 | # store solution 29 | x = np.empty_like(t) 30 | y = np.empty_like(t) 31 | # record initial conditions 32 | x[0] = z0[0] 33 | y[0] = z0[1] 34 | 35 | # solve ODE 36 | for i in range(1,n): 37 | # span for next time step 38 | tspan = [t[i-1],t[i]] 39 | # solve for next step 40 | z = odeint(model,z0,tspan,args=(u[i],)) 41 | # store solution for plotting 42 | x[i] = z[1][0] 43 | y[i] = z[1][1] 44 | # next initial condition 45 | z0 = z[1] 46 | 47 | # plot results 48 | plt.plot(t,u,'g:',label='u(t)') 49 | plt.plot(t,x,'b-',label='x(t)') 50 | plt.plot(t,y,'r--',label='y(t)') 51 | plt.ylabel('values') 52 | plt.xlabel('time') 53 | plt.legend(loc='best') 54 | plt.show() -------------------------------------------------------------------------------- /Module_03/1_Topic/DynamicModeling_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.integrate import odeint 4 | 5 | # define tank model 6 | def tank(Level,time,c,valve): 7 | rho = 1000.0 # water density (kg/m^3) 8 | A = 1.0 # tank area (m^2) 9 | # calculate derivative of the Level 10 | dLevel_dt = (c/(rho*A)) * valve 11 | return dLevel_dt 12 | 13 | # time span for the simulation for 10 sec, every 0.1 sec 14 | ts = np.linspace(0,10,101) 15 | 16 | # valve operation 17 | c = 50.0 # valve coefficient (kg/s / %open) 18 | u = np.zeros(101) # u = valve % open 19 | u[21:70] = 100.0 # open valve between 2 and 7 seconds 20 | 21 | # level initial condition 22 | Level0 = 0 23 | 24 | # for storing the results 25 | z = np.zeros(101) 26 | 27 | # simulate with ODEINT 28 | for i in range(100): 29 | valve = u[i+1] 30 | y = odeint(tank,Level0,[0,0.1],args=(c,valve)) 31 | Level0 = y[-1] # take the last point 32 | z[i+1] = Level0 # store the level for plotting 33 | 34 | # plot results 35 | plt.figure() 36 | plt.subplot(2,1,1) 37 | plt.plot(ts,z,'b-',linewidth=3) 38 | plt.ylabel('Tank Level') 39 | plt.subplot(2,1,2) 40 | plt.plot(ts,u,'r--',linewidth=3) 41 | plt.ylabel('Valve') 42 | plt.xlabel('Time (sec)') 43 | plt.show() -------------------------------------------------------------------------------- /Module_04/3_Assignment/TankBlending_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.integrate import odeint 4 | 5 | # define mixing model 6 | def mixer(x,t,Tf,Caf): 7 | # Inputs (2): 8 | # Tf = Feed Temperature (K) 9 | # Caf = Feed Concentration (mol/m^3) 10 | # States (2): 11 | # Concentration of A (mol/m^3) 12 | Ca = x[0] 13 | # Parameters: 14 | # Volumetric Flowrate (m^3/hr) 15 | q = 100 16 | # Volume of CSTR (m^3) 17 | V = 100 18 | # Calculate concentration derivative 19 | dCadt = q/V*(Caf - Ca) 20 | return dCadt 21 | 22 | # Initial Condition 23 | Ca0 = 0.0 24 | # Feed Temperature (K) 25 | Tf = 300 26 | # Feed Concentration (mol/m^3) 27 | Caf = 1 28 | # Time Interval (min) 29 | t = np.linspace(0,10,100) 30 | 31 | # Simulate mixer 32 | Ca = odeint(mixer,Ca0,t,args=(Tf,Caf)) 33 | 34 | # Construct results and save data file 35 | # Column 1 = time 36 | # Column 2 = concentration 37 | data = np.vstack((t,Ca.T)) # vertical stack 38 | data = data.T # transpose data 39 | np.savetxt('data.txt',data,delimiter=',') 40 | 41 | # Plot the results 42 | plt.plot(t,Ca,'r-',linewidth=3) 43 | plt.ylabel('Ca (mol/L)') 44 | plt.legend(['Concentration'],loc='best') 45 | plt.xlabel('Time (hr)') 46 | plt.show() -------------------------------------------------------------------------------- /Module_13/3_Assignment/ControlBlending_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.integrate import odeint 4 | 5 | # define mixing model 6 | def mixer(x,t,Tf,Caf): 7 | # Inputs (2): 8 | # Tf = Feed Temperature (K) 9 | # Caf = Feed Concentration (mol/L) 10 | # States (2): 11 | # Concentration of A (mol/L) 12 | Ca = x[0] 13 | # Parameters: 14 | # Volumetric Flowrate (m^3/hr) 15 | q = 100 16 | # Volume of CSTR (m^3) 17 | V = 100 18 | # Calculate concentration derivative 19 | dCadt = q/V*(Caf - Ca) 20 | return dCadt 21 | 22 | # Initial Condition 23 | Ca0 = 0.0 24 | # Feed Temperature (K) 25 | Tf = 300 26 | # Feed Concentration (mol/L) 27 | Caf = 1 28 | # Time Interval (min) 29 | t = np.linspace(0,10,100) 30 | 31 | # Simulate mixer 32 | Ca = odeint(mixer,Ca0,t,args=(Tf,Caf)) 33 | 34 | # Construct results and save data file 35 | # Column 1 = time 36 | # Column 2 = concentration 37 | data = np.vstack((t,Ca.T)) # vertical stack 38 | data = data.T # transpose data 39 | np.savetxt('data.txt',data,delimiter=',') 40 | 41 | # Plot the results 42 | plt.plot(t,Ca,'r-',linewidth=3) 43 | plt.ylabel('Ca (mol/L)') 44 | plt.legend(['Concentration'],loc='best') 45 | plt.xlabel('Time (hr)') 46 | plt.show() -------------------------------------------------------------------------------- /Module_17/README.md: -------------------------------------------------------------------------------- 1 | ## Module 17 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Valve Design for Flow Control](https://www.apmonitor.com/pdc/index.php/Main/FlowValves): 0 scripts. Description: Valves regulate fluid flow and are often the final control element for a process control system to maintain pressure, temperature, composition, or other quantities. 6 | ### Quiz 7 | - [Quiz on Flow Valves](https://www.apmonitor.com/pdc/index.php/Main/QuizFlowValves): 0 scripts. Description: Learning assessment on flow valve design and flow control PID tuning. 8 | ### Assignment 9 | - [Valve Design Exercise](https://www.apmonitor.com/pdc/index.php/Main/ValveDesign): 2 scripts. Description: Valve exercise to design the size and type of valve for regulator control. Fluid flow is often the final control element for a process control system to maintain pressure, temperature, composition, or other quantities. 10 | ### TCLab 11 | - [TCLab Heater Actuators](https://www.apmonitor.com/pdc/index.php/Main/TCLabActuator): 5 scripts. Description: The Temperature Control Lab (TCLab) uses TIP31C transistor as heaters to actuate the power dissipation and thereby control the temperature 12 | -------------------------------------------------------------------------------- /Module_26/README.md: -------------------------------------------------------------------------------- 1 | ## Module 26 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [State Space Models](https://www.apmonitor.com/pdc/index.php/Main/StateSpaceModel): 1 scripts. Description: Linear Time Invariant (LTI) state space models are a linear representation of a dynamic system in either discrete or continuous time. Putting a model into state space form is the basis for many methods in process dynamics and control analysis. 6 | ### Quiz 7 | - [Quiz on State Space Models](https://www.apmonitor.com/pdc/index.php/Main/QuizStateSpace): 1 scripts. Description: Learning assessment on state space models. 8 | ### Assignment 9 | - [State Space Stirred Reactor](https://www.apmonitor.com/pdc/index.php/Main/ReactorStateSpace): 2 scripts. Description: Case study on a stirred tank reactor linearization and modeling in Python. The exercise involves creating a dynamic model based on balance equations, linearizing, and fitting the equations into state space form. 10 | ### TCLab 11 | - [TCLab State Space Model](https://www.apmonitor.com/pdc/index.php/Main/TCLabStateSpace): 5 scripts. Description: Derive and simulate a state space model of temperature response to heater step changes 12 | -------------------------------------------------------------------------------- /Module_25/4_TCLab/TCLabBlockDiagram_4.py: -------------------------------------------------------------------------------- 1 | import sympy as sym 2 | from sympy.abc import s,t,x,y,z 3 | import numpy as np 4 | from sympy.integrals import inverse_laplace_transform 5 | import matplotlib.pyplot as plt 6 | 7 | # Define inputs 8 | # Setpoint step (up) starts at 1 sec 9 | TSP = 20/s*sym.exp(-s) 10 | 11 | # Transfer functions 12 | Kc = 2.0 13 | tauI = 180.0 14 | Gc = Kc * (tauI*s+1)/(tauI*s) 15 | delay = 1/(15*s+1) # Taylor series approx 16 | Gp = delay * 0.9/(180*s+1) 17 | 18 | # Closed loop response 19 | Gc = Gc*Gp/(1+Gc*Gp) 20 | 21 | # Calculate response 22 | T1 = Gc * TSP 23 | 24 | # Inverse Laplace Transform 25 | tsp = inverse_laplace_transform(TSP,s,t) 26 | t1 = inverse_laplace_transform(T1,s,t) 27 | print('Temperature Solution') 28 | print(t1) 29 | 30 | # generate data for plot 31 | tm = np.linspace(0,600,100) 32 | TSPplot = np.zeros(len(tm)) 33 | T1plot = np.zeros(len(tm)) 34 | 35 | # substitute numeric values 36 | for i in range(len(tm)): 37 | TSPplot[i] = tsp.subs(t,tm[i]) + 23.0 38 | T1plot[i] = t1.subs(t,tm[i]) + 23.0 39 | 40 | # plot results 41 | plt.figure() 42 | plt.plot(tm/60,TSPplot,'k:',label='T1 Setpoint') 43 | plt.plot(tm/60,T1plot,'b-',label='T1 Predicted') 44 | plt.legend() 45 | plt.xlabel('Time (min)') 46 | plt.ylabel(r'Temperature Change $(\Delta T \, ^oC)$') 47 | plt.show() -------------------------------------------------------------------------------- /Module_05/README.md: -------------------------------------------------------------------------------- 1 | ## Module 5 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Linearization of Differential Equations](https://www.apmonitor.com/pdc/index.php/Main/ModelLinearization): 3 scripts. Description: Linearization is the process of taking the gradient of a nonlinear function with respect to all variables. It is required for certain types of analysis such as a Bode plot, Laplace transforms, and for State Space analysis. 6 | ### Quiz 7 | - [Quiz: Linearize ODEs](https://www.apmonitor.com/pdc/index.php/Main/QuizLinearization): 1 scripts. Description: Learning assessment on linearizing differential equations and transforming the equation into deviation form. 8 | ### Assignment 9 | - [Linearization of Differential Equations](https://www.apmonitor.com/pdc/index.php/Main/LinearizeDiffEqns): 4 scripts. Description: Exercises on linearization of nonlinear differential equations. These exercises take the gradient of a nonlinear function with respect to all variables. 10 | ### TCLab 11 | - [TCLab Linearize Energy Balance](https://www.apmonitor.com/pdc/index.php/Main/TCLabLinearize): 3 scripts. Description: Radiative and Convective Heat Transfer Prediction with a Transistor Heater 12 | -------------------------------------------------------------------------------- /Module_25/4_TCLab/TCLabBlockDiagram_1.py: -------------------------------------------------------------------------------- 1 | import sympy as sym 2 | from sympy.abc import s,t,x,y,z 3 | import numpy as np 4 | from sympy.integrals import inverse_laplace_transform 5 | import matplotlib.pyplot as plt 6 | 7 | # Define inputs 8 | T2 = 20/s*sym.exp(-s) 9 | 10 | # Transfer functions 11 | Kc = 2.0 12 | tauI = 180.0 13 | Gc = Kc * (tauI*s+1)/(tauI*s) 14 | delay = 1/(15*s+1) # Taylor series approx 15 | Gd = delay * 0.3/(180*s+1) 16 | Gp = delay * 0.9/(180*s+1) 17 | 18 | # Closed loop response 19 | Gc = Gd/(1+Gc*Gp) 20 | 21 | # Calculate response 22 | T1 = Gc * T2 23 | 24 | # Inverse Laplace Transform 25 | t2 = inverse_laplace_transform(T2,s,t) 26 | t1 = inverse_laplace_transform(T1,s,t) 27 | print('Temperature Solution') 28 | print(t1) 29 | 30 | # generate data for plot 31 | tm = np.linspace(0,600,100) 32 | T2plot = np.zeros(len(tm)) 33 | T1plot = np.zeros(len(tm)) 34 | 35 | # substitute numeric values 36 | for i in range(len(tm)): 37 | T2plot[i] = t2.subs(t,tm[i]) 38 | T1plot[i] = t1.subs(t,tm[i]) 39 | 40 | # plot results 41 | plt.figure() 42 | plt.plot([0,10],[0,0],'k:',label='T1 Setpoint') 43 | plt.plot(tm/60,T2plot,label='T2 Disturbance') 44 | plt.plot(tm/60,T1plot,label='T1 Predicted') 45 | plt.legend() 46 | plt.xlabel('Time (min)') 47 | plt.ylabel(r'Temperature Change $(\Delta T \, ^oC)$') 48 | plt.show() -------------------------------------------------------------------------------- /Module_24/4_TCLab/TCLabImpulseResponse_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import pandas as pd 4 | import tclab 5 | import time 6 | 7 | try: 8 | # read Step_Response.csv if it exists 9 | data = pd.read_csv('Impulse_Response.csv') 10 | tm = data['Time'].values 11 | Q1 = data['Q1'].values 12 | T1 = data['T1'].values 13 | except: 14 | # generate data only once 15 | n = 120 # Number of second time points (2 min) 16 | tm = np.linspace(0,n,n+1) # Time values 17 | lab = tclab.TCLab() 18 | T1 = [lab.T1] 19 | Q1 = np.zeros(n+1) 20 | Q1[10:71] = 70.0 21 | for i in range(n): 22 | lab.Q1(Q1[i]) 23 | time.sleep(1) 24 | print(lab.T1) 25 | T1.append(lab.T1) 26 | lab.close() 27 | # Save data file 28 | data = np.vstack((tm,Q1,T1)).T 29 | np.savetxt('Impulse_Response.csv',data,delimiter=',',\ 30 | header='Time,Q1,T1',comments='') 31 | 32 | # Create Figure 33 | plt.figure(figsize=(10,7)) 34 | ax = plt.subplot(2,1,1) 35 | ax.grid() 36 | plt.plot(tm,T1,'r.',label=r'$T_1$') 37 | plt.ylabel(r'Temp ($^oC$)') 38 | ax = plt.subplot(2,1,2) 39 | ax.grid() 40 | plt.plot(tm,Q1,'b-',label=r'$Q_1$') 41 | plt.ylabel(r'Heater 1 (%)') 42 | plt.xlabel('Time (sec)') 43 | plt.legend() 44 | plt.savefig('Impulse_Response.png') 45 | plt.show() -------------------------------------------------------------------------------- /Module_21/4_TCLab/ArduinoModeling2_2.py: -------------------------------------------------------------------------------- 1 | clear all; close all; clc 2 | 3 | n = 60*10+1; % Number of second time points (10min) 4 | 5 | % Percent Heater (0-100%) 6 | Q1 = zeros(n,1); 7 | Q2 = zeros(n,1); 8 | % Heater steps 9 | Q1(7:end) = 100.0; % at 0.1 min (6 sec) 10 | Q2(301:end) = 100.0; % at 5.0 min (300 sec) 11 | 12 | % Initial temperature 13 | T0 = 23.0 + 273.15; 14 | 15 | % Store temperature results 16 | T1 = ones(n,1)*T0; 17 | T2 = ones(n,1)*T0; 18 | 19 | time = linspace(0,n-1,n); % Time vector 20 | 21 | for i = 2:n 22 | % initial condition for next step 23 | x0 = [T1(i-1),T2(i-1)]; 24 | % time interval for next step 25 | tm = [time(i-1),time(i)]; 26 | % Integrate ODE for 1 sec each loop 27 | z = ode45(@(t,x)heat2(t,x,Q1(i-1),Q2(i-1)),tm,x0); 28 | % record T1 and T2 at end of simulation 29 | T1(i) = z.y(1,end); 30 | T2(i) = z.y(2,end); 31 | end 32 | 33 | % Plot results 34 | figure(1) 35 | 36 | subplot(2,1,1) 37 | plot(time/60.0,T1-273.15,'b-','LineWidth',2) 38 | hold on 39 | plot(time/60.0,T2-273.15,'r--','LineWidth',2) 40 | ylabel('Temperature (degC)') 41 | legend('T_1','T_2') 42 | 43 | subplot(2,1,2) 44 | plot(time/60.0,Q1,'b-','LineWidth',2) 45 | hold on 46 | plot(time/60.0,Q2,'r--','LineWidth',2) 47 | ylabel('Heater Output') 48 | legend('Q_1','Q_2') 49 | ylim([-10,110]) 50 | xlabel('Time (min)') -------------------------------------------------------------------------------- /Module_24/README.md: -------------------------------------------------------------------------------- 1 | ## Module 24 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Laplace Transforms](https://www.apmonitor.com/pdc/index.php/Main/LaplaceTransforms): 1 scripts. Description: Transfer functions in the Laplace domain help analyze dynamic systems. This introduction shows how to transform a linear differential equation into the Laplace domain and reposition the variables to create a transfer function. 6 | ### Quiz 7 | - [Quiz on Laplace Transforms](https://www.apmonitor.com/pdc/index.php/Main/QuizLaplaceTransforms): 0 scripts. Description: Learning assessment on Laplace transforms with Final and Initial Value Theorem. 8 | ### Assignment 9 | - [Laplace Transform Applications](https://www.apmonitor.com/pdc/index.php/Main/LaplaceApplications): 2 scripts. Description: Express functions or equations in Laplace form. The Laplace exercises are problem that require knowledge of Laplace transforms to develop analytic expressions important in process dynamics and control. 10 | ### TCLab 11 | - [TCLab Impulse Response](https://www.apmonitor.com/pdc/index.php/Main/TCLabImpulseResponse): 2 scripts. Description: Laplace Transform solution of a Heater impulse and Temperature dynamic response with an Arduino 12 | -------------------------------------------------------------------------------- /Module_02/README.md: -------------------------------------------------------------------------------- 1 | ## Module 2 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Solve Differential Equations with ODEINT](https://www.apmonitor.com/pdc/index.php/Main/SolveDifferentialEquations): 6 scripts. Description: A beginning tutorial on solving differential equations with numerical methods in Python. 6 | ### Quiz 7 | - [Quiz: Solve ODEs with Python](https://www.apmonitor.com/pdc/index.php/Main/QuizSolveODEs): 2 scripts. Description: Learning assessment on solving differential equations with numerical methods in Python. 8 | ### Assignment 9 | - [Simulate HIV Infection](https://www.apmonitor.com/pdc/index.php/Main/SimulateHIV): 0 scripts. Description: A beginning tutorial on solving differential equations with numerical methods in Python with HIV simulation in a human body. 10 | - [COVID-19 SEIR Simulation](https://www.apmonitor.com/pdc/index.php/Main/SimulateCOVID19): 5 scripts. Description: Simulate COVID-19 spread on a university campus to determine the effect of social distancing measures. 11 | ### TCLab 12 | - [TCLab Simulate Step Response](https://www.apmonitor.com/pdc/index.php/Main/TCLabSim): 3 scripts. Description: Dynamic Temperature Response of a Heater and Temperature Sensor with an Arduino 13 | -------------------------------------------------------------------------------- /Module_11/README.md: -------------------------------------------------------------------------------- 1 | ## Module 11 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Proportional-only Control](https://www.apmonitor.com/pdc/index.php/Main/ProportionalControl): 0 scripts. Description: Proportional-only control is a less common variant of PID control that does not have an integral or derivative term. It is typically used with integrating processes. 6 | ### Quiz 7 | - [Quiz on Proportional Control](https://www.apmonitor.com/pdc/index.php/Main/QuizProportionalControl): 0 scripts. Description: Learning assessment on proportional control as the most basic type of Proportional Integral Derivative (PID) Control. 8 | ### Assignment 9 | - [Control a Water Tank Level](https://www.apmonitor.com/pdc/index.php/Main/TankLevel): 2 scripts. Description: Proportional-only control is a less common variant of PID control that does not have an integral or derivative term. It is typically used with integrating processes such as this tank level control problem. 10 | ### TCLab 11 | - [TCLab Proportional-only Control](https://www.apmonitor.com/pdc/index.php/Main/TCLabPOnlyControl): 3 scripts. Description: TCLab with proportional-only control has offset between the setpoint and measured temperature. The purpose of this lab activity is to quantify and verify the offset. 12 | -------------------------------------------------------------------------------- /Module_29/1_Topic/ModelSimulation_2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy import signal 3 | import matplotlib.pyplot as plt 4 | from scipy.integrate import odeint 5 | 6 | # tau * dy2/dt2 + 2*zeta*tau*dy/dt + y = Kp*u 7 | Kp = 2.0 # gain 8 | tau = 1.0 # time constant 9 | zeta = 0.25 # damping factor 10 | theta = 0.0 # no time delay 11 | du = 1.0 # change in u 12 | 13 | # (1) Transfer Function 14 | num = [Kp] 15 | den = [tau**2,2*zeta*tau,1] 16 | sys1 = signal.TransferFunction(num,den) 17 | t1,y1 = signal.step(sys1) 18 | 19 | # (2) State Space 20 | A = [[0.0,1.0],[-1.0/tau**2,-2.0*zeta/tau]] 21 | B = [[0.0],[Kp/tau**2]] 22 | C = [1.0,0.0] 23 | D = 0.0 24 | sys2 = signal.StateSpace(A,B,C,D) 25 | t2,y2 = signal.step(sys2) 26 | 27 | # (3) ODE Integrator 28 | def model3(x,t): 29 | y = x[0] 30 | dydt = x[1] 31 | dy2dt2 = (-2.0*zeta*tau*dydt - y + Kp*du)/tau**2 32 | return [dydt,dy2dt2] 33 | t3 = np.linspace(0,25,100) 34 | x3 = odeint(model3,[0,0],t3) 35 | y3 = x3[:,0] 36 | 37 | plt.figure(1) 38 | plt.plot(t1,y1*du,'b--',linewidth=3,label='Transfer Fcn') 39 | plt.plot(t2,y2*du,'g:',linewidth=2,label='State Space') 40 | plt.plot(t3,y3,'r-',linewidth=1,label='ODE Integrator') 41 | y_ss = Kp * du 42 | plt.plot([0,max(t1)],[y_ss,y_ss],'k:') 43 | plt.xlim([0,max(t1)]) 44 | plt.xlabel('Time') 45 | plt.ylabel('Response (y)') 46 | plt.legend(loc='best') 47 | plt.savefig('2nd_order.png') 48 | plt.show() -------------------------------------------------------------------------------- /Module_25/1_Topic/TransferFunctions_1.py: -------------------------------------------------------------------------------- 1 | import sympy as sym 2 | from sympy.abc import s,t,x,y,z 3 | import numpy as np 4 | from sympy.integrals import inverse_laplace_transform 5 | import matplotlib.pyplot as plt 6 | 7 | # Define inputs 8 | # First step (up) starts at 1 sec 9 | U1 = 2/s*sym.exp(-s) 10 | # Ramp (down) starts at 3 sec 11 | U2 = -1/s**2*sym.exp(-3*s) 12 | # Ramp completes at 5 sec 13 | U3 = 1/s**2*sym.exp(-5*s) 14 | 15 | # Transfer function 16 | G = 5*(s+1)/(s+3)**2 17 | 18 | # Calculate responses 19 | Y1 = G * U1 20 | Y2 = G * U2 21 | Y3 = G * U3 22 | 23 | # Inverse Laplace Transform 24 | u1 = inverse_laplace_transform(U1,s,t) 25 | u2 = inverse_laplace_transform(U2,s,t) 26 | u3 = inverse_laplace_transform(U3,s,t) 27 | y1 = inverse_laplace_transform(Y1,s,t) 28 | y2 = inverse_laplace_transform(Y2,s,t) 29 | y3 = inverse_laplace_transform(Y3,s,t) 30 | print('y1') 31 | print(y1) 32 | 33 | # generate data for plot 34 | tm = np.linspace(0,8,100) 35 | us = np.zeros(len(tm)) 36 | ys = np.zeros(len(tm)) 37 | 38 | # substitute numeric values for u and y 39 | for u in [u1,u2,u3]: 40 | for i in range(len(tm)): 41 | us[i] += u.subs(t,tm[i]) 42 | for y in [y1,y2,y3]: 43 | for i in range(len(tm)): 44 | ys[i] += y.subs(t,tm[i]) 45 | 46 | # plot results 47 | plt.figure() 48 | plt.plot(tm,us,label='u(t)') 49 | plt.plot(tm,ys,label='y(t)') 50 | plt.legend() 51 | plt.xlabel('Time') 52 | plt.show() -------------------------------------------------------------------------------- /Module_07/4_TCLab/TCLabRegression_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import pandas as pd 4 | import tclab 5 | import time 6 | 7 | try: 8 | # read Step_Response.csv if it exists 9 | data = pd.read_csv('Step_Response.csv') 10 | tm = data['Time'].values 11 | Q1 = data['Q1'].values 12 | T1 = data['T1'].values 13 | except: 14 | # generate data only once 15 | n = 840 # Number of second time points (14 min) 16 | tm = np.linspace(0,n,n+1) # Time values 17 | lab = tclab.TCLab() 18 | T1 = [lab.T1] 19 | Q1 = np.zeros(n+1) 20 | Q1[30:] = 35.0 21 | Q1[270:] = 70.0 22 | Q1[450:] = 10.0 23 | Q1[630:] = 60.0 24 | Q1[800:] = 0.0 25 | for i in range(n): 26 | lab.Q1(Q1[i]) 27 | time.sleep(1) 28 | print(lab.T1) 29 | T1.append(lab.T1) 30 | lab.close() 31 | # Save data file 32 | data = np.vstack((tm,Q1,T1)).T 33 | np.savetxt('Step_Response.csv',data,delimiter=',',\ 34 | header='Time,Q1,T1',comments='') 35 | 36 | # Create Figure 37 | plt.figure(figsize=(10,7)) 38 | ax = plt.subplot(2,1,1) 39 | ax.grid() 40 | plt.plot(tm/60.0,T1,'r.',label=r'$T_1$') 41 | plt.ylabel(r'Temp ($^oC$)') 42 | ax = plt.subplot(2,1,2) 43 | ax.grid() 44 | plt.plot(tm/60.0,Q1,'b-',label=r'$Q_1$') 45 | plt.ylabel(r'Heater (%)') 46 | plt.xlabel('Time (min)') 47 | plt.legend() 48 | plt.savefig('Step_Response.png') 49 | plt.show() -------------------------------------------------------------------------------- /Module_21/1_Topic/ArduinoModeling_4.py: -------------------------------------------------------------------------------- 1 | #load packages 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | from gekko import GEKKO 5 | 6 | #initialize GEKKO model 7 | m = GEKKO() 8 | 9 | #model discretized time 10 | n = 60*10+1 # Number of second time points (10min) 11 | m.time = np.linspace(0,n-1,n) # Time vector 12 | 13 | # Parameters 14 | Q = m.Param(value=100.0) # Percent Heater (0-100%) 15 | 16 | T0 = m.Param(value=23.0+273.15) # Initial temperature 17 | Ta = m.Param(value=23.0+273.15) # K 18 | U = m.Param(value=10.0) # W/m^2-K 19 | mass = m.Param(value=4.0/1000.0) # kg 20 | Cp = m.Param(value=0.5*1000.0) # J/kg-K 21 | A = m.Param(value=12.0/100.0**2) # Area in m^2 22 | alpha = m.Param(value=0.01) # W / % heater 23 | eps = m.Param(value=0.9) # Emissivity 24 | sigma = m.Const(5.67e-8) # Stefan-Boltzman 25 | 26 | T = m.Var(value=T0) #Temperature state as GEKKO variable 27 | 28 | m.Equation(T.dt() == (1.0/(mass*Cp))*(U*A*(Ta-T) \ 29 | + eps * sigma * A * (Ta**4 - T**4) \ 30 | + alpha*Q)) 31 | 32 | #simulation mode 33 | m.options.IMODE = 4 34 | 35 | #simulation model 36 | m.solve() 37 | 38 | #plot results 39 | plt.figure(1) 40 | plt.plot(m.time/60.0,np.array(T.value)-273.15,'b-') 41 | plt.ylabel('Temperature (degC)') 42 | plt.xlabel('Time (min)') 43 | plt.legend(['Step Test (0-100% heater)']) 44 | plt.show() -------------------------------------------------------------------------------- /Module_25/README.md: -------------------------------------------------------------------------------- 1 | ## Module 25 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Transfer Functions](https://www.apmonitor.com/pdc/index.php/Main/TransferFunctions): 2 scripts. Description: Transfer functions in the Laplace domain help analyze dynamic systems. This introduction shows how to transform a linear differential equation into the Laplace domain and reposition the variables to create a transfer function. 6 | ### Quiz 7 | - [Quiz on Transfer Functions](https://www.apmonitor.com/pdc/index.php/Main/QuizTransferFunctions): 0 scripts. Description: Learning assessment on transfer function analysis including how to calculate gain, compute input to output relationships, and limitations. 8 | ### Assignment 9 | - [Transfer Functions in Block Diagrams](https://www.apmonitor.com/pdc/index.php/Main/TransferFunctionBlockDiagrams): 0 scripts. Description: Transfer functions (Laplace domain) help analyze dynamic systems. Exercises are to transform a linear differential equation into the Laplace domain and reposition the variables to create a transfer function. 10 | ### TCLab 11 | - [TCLab Block Diagram Analysis](https://www.apmonitor.com/pdc/index.php/Main/TCLabBlockDiagram): 5 scripts. Description: Design a controller for automation of temperature regulation to a setpoint. The controller adjusts a heater to regulate the temperature. 12 | -------------------------------------------------------------------------------- /Module_28/3_Assignment/SecondOrderOptimizationFit_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | from scipy.optimize import minimize 5 | 6 | # Import data file 7 | # Column 1 = time (t) 8 | # Column 2 = output (ymeas) 9 | url = 'https://apmonitor.com/pdc/uploads/Main/data_2nd_order.txt' 10 | data = pd.read_csv(url) 11 | t = np.array(data['time']) 12 | ymeas = np.array(data['output (y)']) 13 | 14 | def model(p): 15 | Kp = p[0] 16 | taup = p[1] 17 | # predicted values 18 | ypred = 2.0 * Kp * (1.0-np.exp(-t/taup)) \ 19 | - 4.0 * (1-np.exp(-t)) 20 | return ypred 21 | 22 | def objective(p): 23 | ypred = model(p) 24 | sse = sum((ymeas-ypred)**2) 25 | return sse 26 | 27 | # initial guesses for Kp and taup 28 | p0 = [1.0,1.0] 29 | 30 | # show initial objective 31 | print('Initial SSE Objective: ' + str(objective(p0))) 32 | 33 | # optimize Kp, taup 34 | solution = minimize(objective,p0) 35 | p = solution.x 36 | 37 | # show final objective 38 | print('Final SSE Objective: ' + str(objective(p))) 39 | 40 | print('Kp: ' + str(p[0])) 41 | print('taup: ' + str(p[1])) 42 | 43 | # calculate new ypred 44 | ypred = model(p) 45 | 46 | # plot results 47 | plt.figure() 48 | plt.plot(t,ypred,'r-',linewidth=3,label='Predicted') 49 | plt.plot(t,ymeas,'ko',linewidth=2,label='Measured') 50 | plt.ylabel('Output') 51 | plt.xlabel('Time') 52 | plt.legend(loc='best') 53 | plt.savefig('optimized.png') 54 | plt.show() -------------------------------------------------------------------------------- /Module_07/README.md: -------------------------------------------------------------------------------- 1 | ## Module 7 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Optimization Method: FOPDT to Data](https://www.apmonitor.com/pdc/index.php/Main/FirstOrderOptimization): 2 scripts. Description: A first-order linear system with time delay is a common empirical description of many dynamic processes. This tutorial demonstrates how to fit an FOPDT model to data or a more complex simulation with optimization techniques. 6 | ### Quiz 7 | - [Quiz on Parameter Regression](https://www.apmonitor.com/pdc/index.php/Main/QuizFirstOrderRegression): 1 scripts. Description: Learning assessment on optimizing a first-order plus time-delay model to fit data. The model is fit to data by adjusting three parameters to minimize the sum of squared errors between the predicted and measured values. 8 | ### Assignment 9 | - [Parameter Regression](https://www.apmonitor.com/pdc/index.php/Main/DynamicParameterRegression): 1 scripts. Description: A first-order linear system with time delay is a common empirical description of many dynamic processes. Optimized regression methods are used to fit a first order system to process data. 10 | ### TCLab 11 | - [TCLab FOPDT Regression](https://www.apmonitor.com/pdc/index.php/Main/TCLabRegression): 2 scripts. Description: Linear Dynamic Temperature Response Model of a Heater and Temperature Sensor with an Arduino 12 | -------------------------------------------------------------------------------- /Module_28/3_Assignment/SecondOrderOptimizationFit_2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | from gekko import GEKKO 5 | 6 | # Import data file 7 | # Column 1 = time (t) 8 | # Column 2 = output (ymeas) 9 | url = 'https://apmonitor.com/pdc/uploads/Main/data_2nd_order.txt' 10 | data = pd.read_csv(url) 11 | t = np.array(data['time']) 12 | ymeas = np.array(data['output (y)']) 13 | 14 | # Create GEKKO model 15 | m = GEKKO() 16 | m.time = t 17 | 18 | # Inputs 19 | u = 2 # step input 20 | ym = m.Param(ymeas) 21 | 22 | # estimate parameters Kp and taup 23 | Kp = m.FV() 24 | taup = m.FV(lb=0) 25 | # turn on parameters (STATUS=1) to let optimize change them 26 | Kp.STATUS = 1 27 | taup.STATUS = 1 28 | 29 | # Equation 1 30 | y1 = m.Var(0.0) 31 | m.Equation(taup*y1.dt()==-y1+Kp*u) 32 | 33 | # Equation 2 34 | y2 = m.Var(0.0) 35 | m.Equation(y2.dt()+y2==-2*u) 36 | 37 | # Summation 38 | y = m.Var(0.0) 39 | m.Equation(y==y1+y2) 40 | 41 | # Minimize Objective 42 | m.Obj((y-ym)**2) 43 | 44 | # Optimize Kp, taup 45 | m.options.IMODE=5 46 | m.options.NODES=3 47 | m.solve() 48 | 49 | # show final objective 50 | print('Final SSE Objective: ' + str(m.options.OBJFCNVAL)) 51 | 52 | print('Kp: ' + str(Kp.value[0])) 53 | print('taup: ' + str(taup.value[0])) 54 | 55 | # plot results 56 | plt.figure() 57 | plt.plot(t,y.value,'r-',linewidth=3,label='Predicted') 58 | plt.plot(t,ymeas,'ko',linewidth=2,label='Measured') 59 | plt.ylabel('Output') 60 | plt.xlabel('Time') 61 | plt.legend(loc='best') 62 | plt.savefig('optimized.png') 63 | plt.show() -------------------------------------------------------------------------------- /Module_02/3_Assignment/SimulateCOVID19_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.integrate import odeint 3 | import matplotlib.pyplot as plt 4 | 5 | u = 0.1 # social distancing (0-1) 6 | # 0 = no social distancing 7 | # 0.1 = masks 8 | # 0.2 = masks and hybrid classes 9 | # 0.3 = masks, hybrid, and online classes 10 | 11 | t_incubation = 5.1 12 | t_infective = 3.3 13 | R0 = 2.4 14 | N = 33517 # students 15 | 16 | # initial number of infected and recovered individuals 17 | e0 = 1/N 18 | i0 = 0.00 19 | r0 = 0.00 20 | s0 = 1 - e0 - i0 - r0 21 | x0 = [s0,e0,i0,r0] 22 | 23 | alpha = 1/t_incubation 24 | gamma = 1/t_infective 25 | beta = R0*gamma 26 | 27 | def covid(x,t): 28 | s,e,i,r = x 29 | dx = np.zeros(4) 30 | dx[0] = # dS/dt equation 31 | dx[1] = # dE/dt equation 32 | dx[2] = # dI/dt equation 33 | dx[3] = # dR/dt equation 34 | return dx 35 | 36 | t = np.linspace(0, 200, 101) 37 | x = odeint(covid,x0,t) 38 | s = x[:,0]; e = x[:,1]; i = x[:,2]; r = x[:,3] 39 | 40 | # plot the data 41 | plt.figure(figsize=(8,5)) 42 | 43 | plt.subplot(2,1,1) 44 | plt.title('Social Distancing = '+str(u*100)+'%') 45 | plt.plot(t,s, color='blue', lw=3, label='Susceptible') 46 | plt.plot(t,r, color='red', lw=3, label='Recovered') 47 | plt.ylabel('Fraction') 48 | plt.legend() 49 | 50 | plt.subplot(2,1,2) 51 | plt.plot(t,i, color='orange', lw=3, label='Infective') 52 | plt.plot(t,e, color='purple', lw=3, label='Exposed') 53 | plt.ylim(0, 0.2) 54 | plt.xlabel('Time (days)') 55 | plt.ylabel('Fraction') 56 | plt.legend() 57 | 58 | plt.show() -------------------------------------------------------------------------------- /Module_05/4_TCLab/TCLabLinearize_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.integrate import odeint 4 | import tclab 5 | import time 6 | 7 | n = 300 # Number of second time points (5 min) 8 | 9 | # collect data if TCLab is connected 10 | try: 11 | lab = tclab.TCLab() 12 | T1 = [lab.T1] 13 | lab.Q1(75) 14 | for i in range(n): 15 | time.sleep(1) 16 | print(lab.T1) 17 | T1.append(lab.T1) 18 | lab.close() 19 | connected = True 20 | except: 21 | print('Connect TCLab to Get Data') 22 | connected = False 23 | 24 | # simulation 25 | U = 5.0 26 | A = 0.0012 27 | alpha = 0.01 28 | eps = 0.9 29 | sigma = 5.67e-8 30 | Ta = 23 31 | Cp = 500 32 | m = 0.004 33 | Q = 75 34 | TaK = Ta + 273.15 35 | gamma = -U*A/(m*Cp) - 4*eps*sigma*A*TaK**3/(m*Cp) 36 | beta = alpha/(m*Cp) 37 | def labsim(x,t): 38 | TC,TC2 = x 39 | # convert to Kelvin 40 | TK = TC + 273.15 41 | # nonlinear 42 | dTCdt = (U*A*(Ta-TC) + sigma*eps*A*(TaK**4-TK**4) + alpha*Q)/(m*Cp) 43 | # linear 44 | dTC2dt = gamma * (TC2-23) + beta * (Q-0) 45 | return [dTCdt,dTC2dt] 46 | 47 | tm = np.linspace(0,n,n+1) # Time values 48 | Tsim = odeint(labsim,[23,23],tm) 49 | 50 | T_nonlinear = Tsim[:,0] 51 | T_linear = Tsim[:,1] 52 | 53 | # Plot results 54 | plt.figure() 55 | plt.plot(tm,T_nonlinear,'b-',label='Nonlinear') 56 | plt.plot(tm,T_linear,'k:',label='Linear') 57 | if connected: 58 | plt.plot(tm,T1,'r.',label='Measured') 59 | plt.ylabel(r'Temperature ($^oC$)') 60 | plt.legend() 61 | plt.xlabel('Time (sec)') 62 | plt.show() -------------------------------------------------------------------------------- /Module_17/3_Assignment/ValveDesign_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | ## Lift functions for two different valve trim types 5 | def f_lin(x): 6 | return x # linear valve trim 7 | def f_ep(x): 8 | R = 20 9 | return R**(x-1) # equal percentage valve trim (R = 20-50) 10 | 11 | lift = np.linspace(0,1) # equally spaced points between 0 and 1 12 | 13 | plt.figure(1) # create new figure 14 | plt.title('Valve Performance - Not Installed') 15 | plt.subplot(2,1,1) # 2,1 subplot with 1st window 16 | plt.plot(lift,f_lin(lift),'b-') # linear valve 17 | plt.plot(lift,f_ep(lift),'r--') # equal percentage 18 | plt.ylabel('f(l)') 19 | plt.legend(['Linear Valve Trim','Equal Percentage Valve Trim']) 20 | 21 | g_s = 1.1 # specific gravity of fluid 22 | def q(x,f,Cv,DPv): 23 | return Cv * f(x) * np.sqrt(DPv/g_s) # flow through a valve 24 | 25 | ## Intrinsic valve performance 26 | # no process equipment - all pressure drop is across valve 27 | DPt = 100 # Total pressure generated by pump (constant) 28 | Cv = 2 # Valve Cv 29 | flow_lin = q(lift,f_lin,Cv,DPt) # flow with linear valve 30 | flow_ep = q(lift,f_ep,Cv,DPt) # flow with equal percentage 31 | 32 | plt.subplot(2,1,2) # 2,1 subplot with 2nd window 33 | plt.plot(lift,flow_lin,'b-') # plot linear valve response 34 | plt.plot(lift,flow_ep,'r--') # plot equal percentage valve 35 | plt.ylabel('Flow') 36 | plt.legend(['Linear Valve Trim','Equal Percentage Valve Trim']) 37 | plt.xlabel('Fractional Valve Lift') 38 | plt.show() -------------------------------------------------------------------------------- /Module_02/3_Assignment/SimulateCOVID19_4.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.integrate import odeint 3 | import matplotlib.pyplot as plt 4 | 5 | u = 0.1 # social distancing (0-1) 6 | # 0 = no social distancing 7 | # 0.1 = masks 8 | # 0.2 = masks and hybrid classes 9 | # 0.3 = masks, hybrid, and online classes 10 | 11 | t_incubation = 5.1 12 | t_infective = 3.3 13 | R0 = 2.4 14 | N = 33517 # students 15 | 16 | # initial number of infected and recovered individuals 17 | e0 = 1/N 18 | i0 = 0.00 19 | r0 = 0.00 20 | s0 = 1 - e0 - i0 - r0 21 | x0 = [s0,e0,i0,r0] 22 | 23 | alpha = 1/t_incubation 24 | gamma = 1/t_infective 25 | beta = R0*gamma 26 | 27 | def covid(x,t): 28 | s,e,i,r = x 29 | dx = np.zeros(4) 30 | dx[0] = -(1-u)*beta * s * i 31 | dx[1] = (1-u)*beta * s * i - alpha * e 32 | dx[2] = alpha * e - gamma * i 33 | dx[3] = gamma*i 34 | return dx 35 | 36 | t = np.linspace(0, 200, 101) 37 | x = odeint(covid,x0,t) 38 | s = x[:,0]; e = x[:,1]; i = x[:,2]; r = x[:,3] 39 | 40 | # plot the data 41 | plt.figure(figsize=(8,5)) 42 | 43 | plt.subplot(2,1,1) 44 | plt.title('Social Distancing = '+str(u*100)+'%') 45 | plt.plot(t,s, color='blue', lw=3, label='Susceptible') 46 | plt.plot(t,r, color='red', lw=3, label='Recovered') 47 | plt.ylabel('Fraction') 48 | plt.legend() 49 | 50 | plt.subplot(2,1,2) 51 | plt.plot(t,i, color='orange', lw=3, label='Infective') 52 | plt.plot(t,e, color='purple', lw=3, label='Exposed') 53 | plt.ylim(0, 0.2) 54 | plt.xlabel('Time (days)') 55 | plt.ylabel('Fraction') 56 | plt.legend() 57 | 58 | plt.show() -------------------------------------------------------------------------------- /Module_22/4_TCLab/ArduinoEstimation2_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import tclab 4 | import time 5 | import os.path 6 | 7 | # generate step test data on Arduino 8 | filename = 'data.csv' 9 | 10 | # redo data collection? 11 | redo = False 12 | 13 | # check if file already exists 14 | if os.path.isfile(filename) and (not redo): 15 | print('File: '+filename+' already exists.') 16 | print('Change redo=True to collect data again') 17 | print('TCLab should be at room temperature at start') 18 | else: 19 | # heater steps 20 | Q1d = np.zeros(601) 21 | Q1d[10:200] = 80 22 | Q1d[200:280] = 20 23 | Q1d[280:400] = 70 24 | Q1d[400:] = 50 25 | 26 | Q2d = np.zeros(601) 27 | Q2d[120:320] = 100 28 | Q2d[320:520] = 10 29 | Q2d[520:] = 80 30 | 31 | # Connect to Arduino 32 | a = tclab.TCLab() 33 | fid = open(filename,'w') 34 | fid.write('Time,Q1,Q2,T1,T2\n') 35 | fid.close() 36 | 37 | # run step test (10 min) 38 | for i in range(601): 39 | # set heater values 40 | a.Q1(Q1d[i]) 41 | a.Q2(Q2d[i]) 42 | print('Time: ' + str(i) + \ 43 | ' Q1: ' + str(Q1d[i]) + \ 44 | ' Q2: ' + str(Q2d[i]) + \ 45 | ' T1: ' + str(a.T1) + \ 46 | ' T2: ' + str(a.T2)) 47 | # wait 1 second 48 | time.sleep(1) 49 | fid = open(filename,'a') 50 | fid.write(str(i)+','+str(Q1d[i])+','+str(Q2d[i])+',' \ 51 | +str(a.T1)+','+str(a.T2)+'\n') 52 | # close connection to Arduino 53 | a.close() 54 | fid.close() -------------------------------------------------------------------------------- /Module_18/README.md: -------------------------------------------------------------------------------- 1 | ## Module 18 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Sensors and Signals for Process Control](https://www.apmonitor.com/pdc/index.php/Main/SensorSignals): 0 scripts. Description: Pumps and valves regulate fluid flow and are often the final control element for a process control system. Analog control signals are often sent 3-15 psig levels to valves and sensors return 4-20 mA signals to indicate the fraction of sensor range. 6 | ### Quiz 7 | - [Quiz on Sensors and Data Acquisition](https://www.apmonitor.com/pdc/index.php/Main/QuizSensors): 0 scripts. Description: Learning assessment on sensors and data acquisition. 8 | ### Assignment 9 | - [Transmitter Design Exercise](https://www.apmonitor.com/pdc/index.php/Main/SensorDesign): 0 scripts. Description: Sensors convert process conditions into either analog or digital signals. Flow, presssure, level, temperature, and concentration sensors are reviewed. 10 | ### TCLab 11 | - [TCLab Temperature Sensor](https://www.apmonitor.com/pdc/index.php/Main/TCLabSensor): 3 scripts. Description: Dynamic Temperature Response of a Heater and Temperature Sensor with an Arduino 12 | ## Module 18 in Process Dynamics and Control 13 | - [Course Overview](https://apmonitor.com/pdc) 14 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 15 | ### Topic 16 | - [Data Acquisition (DAQ)](https://www.apmonitor.com/pdc/index.php/Main/DataAcquisition): 0 scripts. Description: Python tutorial for collecting data for dynamic simulation, estimation, and control 17 | -------------------------------------------------------------------------------- /Module_04/4_TCLab/TCLabRadiative_2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.integrate import odeint 4 | import tclab 5 | import time 6 | 7 | n = 300 # Number of second time points (5 min) 8 | 9 | # collect data if TCLab is connected 10 | try: 11 | lab = tclab.TCLab() 12 | T1 = [lab.T1] 13 | lab.Q1(50) 14 | for i in range(n): 15 | time.sleep(1) 16 | print(lab.T1) 17 | T1.append(lab.T1) 18 | lab.close() 19 | connected = True 20 | except: 21 | print('Connect TCLab to Get Data') 22 | connected = False 23 | 24 | # simulation 25 | U = 5.0 26 | A = 0.0012 27 | alpha = 0.01 28 | eps = 0.9 29 | sigma = 5.67e-8 30 | Ta = 23 31 | Cp = 500 32 | m = 0.004 33 | TaK = Ta + 273.15 34 | def labsim(TC,t): 35 | TK = TC + 273.15 36 | dTCdt = (U*A*(Ta-TC) + sigma*eps*A*(TaK**4-TK**4) + alpha*50)/(m*Cp) 37 | return dTCdt 38 | 39 | tm = np.linspace(0,n,n+1) # Time values 40 | Tsim = odeint(labsim,23,tm) 41 | 42 | # calculate losses from conv and rad 43 | conv = U*A*(Ta-Tsim) 44 | rad = sigma*eps*A*(TaK**4-(Tsim+273.15)**4) 45 | loss = conv+rad 46 | gain = alpha*50 47 | 48 | # Plot results 49 | plt.figure() 50 | plt.subplot(2,1,1) 51 | plt.plot(tm,Tsim,'b-',label='Simulated') 52 | if connected: 53 | plt.plot(tm,T1,'r.',label='Measured') 54 | plt.ylabel(r'Temperature ($^oC$)') 55 | plt.legend() 56 | plt.subplot(2,1,2) 57 | plt.plot(tm,conv,'g:',label='Convection') 58 | plt.plot(tm,rad,'r--',label='Radiation') 59 | plt.plot(tm,loss,'k-',label='Total Lost') 60 | plt.text(150,-0.1,'Heater input = '+str(gain)+' W') 61 | plt.ylabel(r'Heat Loss (W)') 62 | plt.legend(loc=3) 63 | plt.xlabel('Time (sec)') 64 | plt.show() -------------------------------------------------------------------------------- /Module_05/4_TCLab/TCLabLinearize_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.integrate import odeint 4 | import tclab 5 | import time 6 | 7 | n = 300 # Number of second time points (5 min) 8 | 9 | # collect data if TCLab is connected 10 | try: 11 | lab = tclab.TCLab() 12 | T1 = [lab.T1] 13 | lab.Q1(75) 14 | for i in range(n): 15 | time.sleep(1) 16 | print(lab.T1) 17 | T1.append(lab.T1) 18 | lab.close() 19 | connected = True 20 | except: 21 | print('Connect TCLab to Get Data') 22 | connected = False 23 | 24 | # simulation 25 | U = 5.0 26 | A = 0.0012 27 | alpha = 0.01 28 | eps = 0.9 29 | sigma = 5.67e-8 30 | Ta = 23 31 | Cp = 500 32 | m = 0.004 33 | TaK = Ta + 273.15 34 | def labsim(TC,t): 35 | TK = TC + 273.15 36 | dTCdt = (U*A*(Ta-TC) + sigma*eps*A*(TaK**4-TK**4) + alpha*50)/(m*Cp) 37 | return dTCdt 38 | 39 | tm = np.linspace(0,n,n+1) # Time values 40 | Tsim = odeint(labsim,23,tm) 41 | 42 | # calculate losses from conv and rad 43 | conv = U*A*(Ta-Tsim) 44 | rad = sigma*eps*A*(TaK**4-(Tsim+273.15)**4) 45 | loss = conv+rad 46 | gain = alpha*50 47 | 48 | # Plot results 49 | plt.figure() 50 | plt.subplot(2,1,1) 51 | plt.plot(tm,Tsim,'b-',label='Simulated') 52 | if connected: 53 | plt.plot(tm,T1,'r.',label='Measured') 54 | plt.ylabel(r'Temperature ($^oC$)') 55 | plt.legend() 56 | plt.subplot(2,1,2) 57 | plt.plot(tm,conv,'g:',label='Convection') 58 | plt.plot(tm,rad,'r--',label='Radiation') 59 | plt.plot(tm,loss,'k-',label='Total Lost') 60 | plt.text(150,-0.1,'Heater input = '+str(gain)+' W') 61 | plt.ylabel(r'Heat Loss (W)') 62 | plt.legend(loc=3) 63 | plt.xlabel('Time (sec)') 64 | plt.show() -------------------------------------------------------------------------------- /Module_24/4_TCLab/TCLabImpulseResponse_2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import pandas as pd 4 | import tclab 5 | import time 6 | 7 | try: 8 | # read Step_Response.csv if it exists 9 | data = pd.read_csv('Impulse_Response.csv') 10 | tm = data['Time'].values 11 | Q1 = data['Q1'].values 12 | T1 = data['T1'].values 13 | except: 14 | # generate data only once 15 | n = 120 # Number of second time points (2 min) 16 | tm = np.linspace(0,n,n+1) # Time values 17 | lab = tclab.TCLab() 18 | T1 = [lab.T1] 19 | Q1 = np.zeros(n+1) 20 | Q1[10:71] = 70.0 21 | for i in range(n): 22 | lab.Q1(Q1[i]) 23 | time.sleep(1) 24 | print(lab.T1) 25 | T1.append(lab.T1) 26 | lab.close() 27 | # Save data file 28 | data = np.vstack((tm,Q1,T1)).T 29 | np.savetxt('Impulse_Response.csv',data,delimiter=',',\ 30 | header='Time,Q1,T1',comments='') 31 | 32 | # Analytic solution 33 | nt = len(tm) 34 | Kp = 0.9 35 | taup = 190.0 36 | s1 = np.zeros(nt) 37 | s1[25:] = 1.0 38 | s2 = np.zeros(nt) 39 | s2[85:] = 1.0 40 | T1s = 70 * Kp * (1.0-np.exp(-(tm-25.0)/taup))*s1 - \ 41 | 70 * Kp * (1.0-np.exp(-(tm-85.0)/taup))*s2 + T1[0] 42 | 43 | # Create Figure 44 | plt.figure(figsize=(10,7)) 45 | ax = plt.subplot(2,1,1) 46 | ax.grid() 47 | plt.plot(tm,T1,'r.',label=r'$T_1$ measured') 48 | plt.plot(tm,T1s,'b:',label=r'$T_1$ predicted') 49 | plt.ylabel(r'Temp ($^oC$)') 50 | plt.legend() 51 | ax = plt.subplot(2,1,2) 52 | ax.grid() 53 | plt.plot(tm,Q1,'b-',label=r'$Q_1$') 54 | plt.ylabel(r'Heater 1 (%)') 55 | plt.xlabel('Time (sec)') 56 | plt.legend() 57 | plt.savefig('Impulse_Response.png') 58 | plt.show() -------------------------------------------------------------------------------- /Module_31/README.md: -------------------------------------------------------------------------------- 1 | ## Module 31 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Cascade Control](https://www.apmonitor.com/pdc/index.php/Main/CascadeControl): 0 scripts. Description: Cascade control is implemented as a hierarchy of control loops that successively provide set points to lower levels. 6 | ### Quiz 7 | - [Quiz on Feedforward and Cascade Control](https://www.apmonitor.com/pdc/index.php/Main/QuizFeedforwardCascade): 0 scripts. Description: Learning assessment on feedforward and cascade control. 8 | ### Assignment 9 | - [Feedforward or Cascade Control](https://www.apmonitor.com/pdc/index.php/Main/FeedforwardCascadeControl): 5 scripts. Description: Feedforward and cascade control are two control configurations to reject process disturbances. This example problem shows how to implement feedforward and cascade control for level control. 10 | ### TCLab 11 | - [TCLab Cascade Control](https://www.apmonitor.com/pdc/index.php/Main/TCLabCascadeControl): 4 scripts. Description: Tune a cascade controller for the Temperature Control Lab by selecting controller gains and time constants. 12 | ## Module 31 in Process Dynamics and Control 13 | - [Course Overview](https://apmonitor.com/pdc) 14 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 15 | ### Topic 16 | - [Feedforward Control](https://www.apmonitor.com/pdc/index.php/Main/FeedforwardControl): 0 scripts. Description: Feedforward control is implemented to reject disturbances and anticipate the effect on the systems before it shows up as a deviation from the set point. 17 | -------------------------------------------------------------------------------- /Module_26/1_Topic/StateSpaceModel_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy import signal 3 | 4 | # problem 1 5 | A = [-4.0] 6 | B = [2.0] 7 | C = [1.0] 8 | D = [0.0] 9 | sys1 = signal.StateSpace(A,B,C,D) 10 | t1,y1 = signal.step(sys1) 11 | 12 | # problem 2 13 | A = [[-3.0,0.0],[-2.0,-3.0]] 14 | print(np.linalg.eig(A)[0]) 15 | B = [[4.0],[0.0]] 16 | C = [0.5,0.5] 17 | D = [0.0] 18 | sys2 = signal.StateSpace(A,B,C,D) 19 | t2,y2 = signal.step(sys2) 20 | 21 | # problem 3 22 | A = [[0.0,1.0],[-0.25,-0.5]] 23 | print(np.linalg.eig(A)[0]) 24 | B = [[0.0],[0.75]] 25 | C = [1.0,0.0] 26 | D = [0.0] 27 | sys3 = signal.StateSpace(A,B,C,D) 28 | t = np.linspace(0,30,100) 29 | u = np.zeros(len(t)) 30 | u[5:50] = 1.0 # first step input 31 | u[50:] = 2.0 # second step input 32 | t3,y3,x3 = signal.lsim(sys3,u,t) 33 | 34 | # problem 4 35 | A = [[-2.0,-6.0],[-8.0,-8.0]] 36 | print(np.linalg.eig(A)[0]) 37 | B = [[8.0],[0.0]] 38 | C = [[0.0,1.0],[1.0,0.0]] 39 | D = [[0.0],[0.0]] 40 | sys4 = signal.StateSpace(A,B,C,D) 41 | t4,y4 = signal.step(sys4) 42 | 43 | import matplotlib.pyplot as plt 44 | plt.figure(1) 45 | plt.subplot(4,1,1) 46 | plt.plot(t1,y1,'r-',linewidth=3) 47 | plt.ylabel('Problem 1') 48 | plt.legend(['y'],loc='best') 49 | plt.subplot(4,1,2) 50 | plt.plot(t2,y2,'b--',linewidth=3) 51 | plt.ylabel('Problem 2') 52 | plt.legend(['y'],loc='best') 53 | plt.subplot(4,1,3) 54 | plt.plot(t3,y3,'k-',linewidth=3) 55 | plt.plot(t,u,'r-') 56 | plt.ylabel('Problem 3') 57 | plt.legend(['y','u'],loc='best') 58 | plt.subplot(4,1,4) 59 | plt.plot(t4,y4[:,0]+2.0,'r--',linewidth=3) 60 | plt.plot(t4,y4[:,1]+2.0,'b-',linewidth=3) 61 | plt.legend(['y1','y2'],loc='best') 62 | plt.ylabel('Problem 4') 63 | plt.xlabel('Time') 64 | plt.show() -------------------------------------------------------------------------------- /Module_04/4_TCLab/TCLabRadiative_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | # pip install gekko 6 | from gekko import GEKKO 7 | 8 | n = 300 # Number of second time points (5 min) 9 | 10 | # collect data if TCLab is connected 11 | try: 12 | lab = tclab.TCLab() 13 | T1 = [lab.T1] 14 | lab.Q1(50) 15 | for i in range(n): 16 | time.sleep(1) 17 | print(lab.T1) 18 | T1.append(lab.T1) 19 | lab.close() 20 | connected = True 21 | except: 22 | print('Connect TCLab to Get Data') 23 | connected = False 24 | 25 | # simulation 26 | m = GEKKO() 27 | m.time = np.linspace(0,n,n+1) 28 | U = 5.0; A = 0.0012; Cp = 500 29 | mass = 0.004; alpha = 0.01; Ta = 23 30 | eps = 0.9; sigma = 5.67e-8; TaK = Ta+273.15 31 | TC = m.Var(Ta) 32 | TK = m.Intermediate(TC+273.15) 33 | conv = m.Intermediate(U*A*(Ta-TC)) 34 | rad = m.Intermediate(sigma*eps*A*(TaK**4-TK**4)) 35 | loss = m.Intermediate(conv + rad) 36 | gain = m.Intermediate(alpha*50) 37 | m.Equation(mass*Cp*TC.dt()==conv+rad+gain) 38 | m.options.NODES = 3 39 | m.options.IMODE = 4 # dynamic simulation 40 | m.solve(disp=False) 41 | 42 | # Plot results 43 | plt.figure() 44 | plt.subplot(2,1,1) 45 | plt.plot(m.time,TC,'b-',label='Simulated') 46 | if connected: 47 | plt.plot(m.time,T1,'r.',label='Measured') 48 | plt.ylabel(r'Temperature ($^oC$)') 49 | plt.legend() 50 | plt.subplot(2,1,2) 51 | plt.plot(m.time,conv,'g:',label='Convection') 52 | plt.plot(m.time,rad,'r--',label='Radiation') 53 | plt.plot(m.time,loss,'k-',label='Total Lost') 54 | plt.text(150,-0.1,'Heater input = '+str(gain.value[0])+' W') 55 | plt.ylabel(r'Heat Loss (W)') 56 | plt.legend(loc=3) 57 | plt.xlabel('Time (sec)') 58 | plt.show() -------------------------------------------------------------------------------- /Module_05/4_TCLab/TCLabLinearize_2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | # pip install gekko 6 | from gekko import GEKKO 7 | 8 | n = 300 # Number of second time points (5 min) 9 | 10 | # collect data if TCLab is connected 11 | try: 12 | lab = tclab.TCLab() 13 | T1 = [lab.T1] 14 | lab.Q1(75) 15 | for i in range(n): 16 | time.sleep(1) 17 | print(lab.T1) 18 | T1.append(lab.T1) 19 | lab.close() 20 | connected = True 21 | except: 22 | print('Connect TCLab to Get Data') 23 | connected = False 24 | 25 | # simulation 26 | m = GEKKO() 27 | m.time = np.linspace(0,n,n+1) 28 | U = 5.0; A = 0.0012; Cp = 500 29 | mass = 0.004; alpha = 0.01; Ta = 23 30 | eps = 0.9; sigma = 5.67e-8; TaK = Ta+273.15 31 | TC = m.Var(Ta) 32 | TK = m.Intermediate(TC+273.15) 33 | conv = m.Intermediate(U*A*(Ta-TC)) 34 | rad = m.Intermediate(sigma*eps*A*(TaK**4-TK**4)) 35 | loss = m.Intermediate(conv + rad) 36 | gain = m.Intermediate(alpha*50) 37 | m.Equation(mass*Cp*TC.dt()==conv+rad+gain) 38 | m.options.NODES = 3 39 | m.options.IMODE = 4 # dynamic simulation 40 | m.solve(disp=False) 41 | 42 | # Plot results 43 | plt.figure() 44 | plt.subplot(2,1,1) 45 | plt.plot(m.time,TC,'b-',label='Simulated') 46 | if connected: 47 | plt.plot(m.time,T1,'r.',label='Measured') 48 | plt.ylabel(r'Temperature ($^oC$)') 49 | plt.legend() 50 | plt.subplot(2,1,2) 51 | plt.plot(m.time,conv,'g:',label='Convection') 52 | plt.plot(m.time,rad,'r--',label='Radiation') 53 | plt.plot(m.time,loss,'k-',label='Total Lost') 54 | plt.text(150,-0.1,'Heater input = '+str(gain.value[0])+' W') 55 | plt.ylabel(r'Heat Loss (W)') 56 | plt.legend(loc=3) 57 | plt.xlabel('Time (sec)') 58 | plt.show() -------------------------------------------------------------------------------- /Module_05/1_Topic/ModelLinearization_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.integrate import odeint 3 | import matplotlib.pyplot as plt 4 | 5 | # function that returns dz/dt 6 | def model(z,t,u): 7 | x1 = z[0] 8 | x2 = z[1] 9 | dx1dt = -x1**2 + np.sqrt(u) 10 | dx2dt = -4.0*(x2-2.0) + (1.0/8.0)*(u-16.0) 11 | dzdt = [dx1dt,dx2dt] 12 | return dzdt 13 | 14 | # steady state conditions 15 | x_ss = 2.0 16 | u_ss = 16.0 17 | 18 | # initial condition 19 | z0 = [x_ss,x_ss] 20 | 21 | # final time 22 | tf = 10 23 | 24 | # number of time points 25 | n = tf * 10 + 1 26 | 27 | # time points 28 | t = np.linspace(0,tf,n) 29 | 30 | # step input 31 | u = np.ones(n) * u_ss 32 | # magnitude of step 33 | m = 8.0 34 | # change up m at time = 1.0 35 | u[11:] = u[11:] + m 36 | # change down 2*m at time = 4.0 37 | u[41:] = u[41:] - 2.0 * m 38 | # change up m at time = 7.0 39 | u[71:] = u[71:] + m 40 | 41 | # store solution 42 | x1 = np.empty_like(t) 43 | x2 = np.empty_like(t) 44 | # record initial conditions 45 | x1[0] = z0[0] 46 | x2[0] = z0[1] 47 | 48 | # solve ODE 49 | for i in range(1,n): 50 | # span for next time step 51 | tspan = [t[i-1],t[i]] 52 | # solve for next step 53 | z = odeint(model,z0,tspan,args=(u[i],)) 54 | # store solution for plotting 55 | x1[i] = z[1][0] 56 | x2[i] = z[1][1] 57 | # next initial condition 58 | z0 = z[1] 59 | 60 | # plot results 61 | plt.figure(1) 62 | plt.subplot(2,1,1) 63 | plt.plot(t,u,'g-',linewidth=3,label='u(t) Doublet Test') 64 | plt.grid() 65 | plt.legend(loc='best') 66 | plt.subplot(2,1,2) 67 | plt.plot(t,x1,'b-',linewidth=3,label='x(t) Nonlinear') 68 | plt.plot(t,x2,'r--',linewidth=3,label='x(t) Linear') 69 | plt.xlabel('time') 70 | plt.grid() 71 | plt.legend(loc='best') 72 | plt.show() -------------------------------------------------------------------------------- /Module_11/4_TCLab/TCLabPOnlyControl_2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | 6 | # ----------------------------- 7 | # Adjust controller gain (Kc) 8 | # from ITAE tuning correlation 9 | # ----------------------------- 10 | Kc = 11 | 12 | n = 600 # Number of second time points (10 min) 13 | tm = np.linspace(0,n-1,n) # Time values 14 | lab = tclab.TCLab() 15 | T1 = np.zeros(n) 16 | Q1 = np.zeros(n) 17 | # step setpoint from 23.0 to 60.0 degC 18 | SP1 = np.ones(n)*23.0 19 | SP1[10:] = 60.0 20 | Q1_bias = 0.0 21 | for i in range(n): 22 | # record measurement 23 | T1[i] = lab.T1 24 | 25 | # -------------------------------------------------- 26 | # fill-in P-only controller equation to change Q1[i] 27 | # -------------------------------------------------- 28 | Q1[i] = 29 | 30 | # implement new heater value 31 | Q1[i] = max(0,min(100,Q1[i])) # clip to 0-100% 32 | lab.Q1(Q1[i]) 33 | if i%20==0: 34 | print(' Heater, Temp, Setpoint') 35 | print(f'{Q1[i]:7.2f},{T1[i]:7.2f},{SP1[i]:7.2f}') 36 | # wait for 1 sec 37 | time.sleep(1) 38 | lab.close() 39 | # Save data file 40 | data = np.vstack((tm,Q1,T1,SP1)).T 41 | np.savetxt('P-only.csv',data,delimiter=',',\ 42 | header='Time,Q1,T1,SP1',comments='') 43 | 44 | # Create Figure 45 | plt.figure(figsize=(10,7)) 46 | ax = plt.subplot(2,1,1) 47 | ax.grid() 48 | plt.plot(tm/60.0,SP1,'k-',label=r'$T_1$ SP') 49 | plt.plot(tm/60.0,T1,'r.',label=r'$T_1$ PV') 50 | plt.ylabel(r'Temp ($^oC$)') 51 | plt.legend(loc=2) 52 | ax = plt.subplot(2,1,2) 53 | ax.grid() 54 | plt.plot(tm/60.0,Q1,'b-',label=r'$Q_1$') 55 | plt.ylabel(r'Heater (%)') 56 | plt.xlabel('Time (min)') 57 | plt.legend(loc=1) 58 | plt.savefig('P-only_Control.png') 59 | plt.show() -------------------------------------------------------------------------------- /Module_17/3_Assignment/ValveDesign_2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | DPt = 100 # Total pressure generated by pump (constant) 5 | Cv = 2 # Valve Cv 6 | g_s = 1.1 # specific gravity of fluid 7 | lift = np.linspace(0,1) # equally spaced points between 0 and 1 8 | 9 | ## Lift functions for two different valve trim types 10 | def f_lin(x): 11 | return x # linear valve trim 12 | def f_ep(x): 13 | R = 20 14 | return R**(x-1) # equal percentage valve trim (R = 20-50) 15 | 16 | ## Installed valve performance 17 | # pressure drop across the system (without valve) 18 | c1 = 2 19 | def DPe(q): 20 | return c1 * q**2 21 | 22 | # valve and process equipment flow with 100 bar pressure drop 23 | def qi(x,f,Cv): 24 | return np.sqrt((Cv*f(x))**2*DPt / (g_s + (Cv*f(x))**2 * c1)) 25 | 26 | # Process equipment + Valve performance 27 | flow_lin = qi(lift,f_lin,Cv) # flow through linear valve 28 | flow_ep = qi(lift,f_ep,Cv) # flow through equal percentage valve 29 | 30 | plt.figure(2) 31 | plt.title('Valve Performance - Installed') 32 | plt.subplot(3,1,1) 33 | plt.plot(lift,flow_lin,'b-',label='Linear Valve') 34 | plt.plot(lift,flow_ep,'r--',label='Equal Percentage Valve') 35 | plt.plot([0,1],[0,6.5],'k-',linewidth=2,label='Desired Profile') 36 | plt.legend(loc='best') 37 | plt.ylabel('Flow') 38 | 39 | plt.subplot(3,1,2) 40 | plt.plot(lift,DPt-DPe(flow_lin),'k:',linewidth=3) 41 | plt.plot(lift,DPe(flow_lin),'r--',linewidth=3) 42 | plt.legend(['Linear Valve','Process Equipment'],loc='best') 43 | plt.ylabel(r'$\Delta P$') 44 | 45 | plt.subplot(3,1,3) 46 | plt.plot(lift,DPt-DPe(flow_ep),'k:',linewidth=3) 47 | plt.plot(lift,DPe(flow_ep),'r--',linewidth=3) 48 | plt.legend(['Equal Percentage Valve','Process Equipment'],loc='best') 49 | plt.ylabel(r'$\Delta P$') 50 | plt.xlabel('Lift') 51 | 52 | plt.show() -------------------------------------------------------------------------------- /Module_02/3_Assignment/SimulateCOVID19_5.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from gekko import GEKKO 3 | import matplotlib.pyplot as plt 4 | 5 | m = GEKKO() 6 | u = m.FV(0.1) # social distancing (0-1) 7 | # 0 = no social distancing 8 | # 0.1 = masks 9 | # 0.2 = masks and hybrid classes 10 | # 0.3 = masks, hybrid, and online classes 11 | 12 | t_incubation = 5.1 13 | t_infective = 3.3 14 | R0 = 2.4 15 | N = 33517 16 | 17 | # initial number of infected and recovered individuals 18 | e_initial = 1/N 19 | i_initial = 0.00 20 | r_initial = 0.00 21 | s_initial = 1 - e_initial - i_initial - r_initial 22 | 23 | alpha = 1/t_incubation 24 | gamma = 1/t_infective 25 | beta = R0*gamma 26 | 27 | s,e,i,r = m.Array(m.Var,4) 28 | s.value = s_initial 29 | e.value = e_initial 30 | i.value = i_initial 31 | s.value = s_initial 32 | m.Equations([s.dt()==-(1-u)*beta * s * i,\ 33 | e.dt()== (1-u)*beta * s * i - alpha * e,\ 34 | i.dt()==alpha * e - gamma * i,\ 35 | r.dt()==gamma*i]) 36 | 37 | t = np.linspace(0, 200, 101) 38 | t = np.insert(t,1,[0.001,0.002,0.004,0.008,0.02,0.04,0.08,\ 39 | 0.2,0.4,0.8]) 40 | m.time = t 41 | m.options.IMODE=7 42 | m.options.NODES=3 43 | m.solve(disp=False) 44 | 45 | # plot the data 46 | plt.figure(figsize=(8,5)) 47 | plt.subplot(2,1,1) 48 | plt.title('Social Distancing = '+str(u.value[0]*100)+'%') 49 | plt.plot(m.time, s.value, color='blue', lw=3, label='Susceptible') 50 | plt.plot(m.time, r.value, color='red', lw=3, label='Recovered') 51 | plt.ylabel('Fraction') 52 | plt.legend() 53 | 54 | plt.subplot(2,1,2) 55 | plt.plot(m.time, i.value, color='orange', lw=3, label='Infective') 56 | plt.plot(m.time, e.value, color='purple', lw=3, label='Exposed') 57 | plt.ylim(0, 0.2) 58 | plt.xlabel('Time (days)') 59 | plt.ylabel('Fraction') 60 | plt.legend() 61 | 62 | plt.show() -------------------------------------------------------------------------------- /Module_05/3_Assignment/LinearizeDiffEqns_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.integrate import odeint 4 | 5 | # define model 6 | def vehicle(v,t,u,load): 7 | # inputs 8 | # v = vehicle velocity (m/s) 9 | # t = time (sec) 10 | # u = gas pedal position (0% to 100%) 11 | # load = passenger load + cargo (kg) 12 | Cd = 0.24 # drag coefficient 13 | rho = 1.225 # air density (kg/m^3) 14 | A = 5.0 # cross-sectional area (m^2) 15 | Fp = 30 # thrust parameter (N/%pedal) 16 | m = 500 # vehicle mass (kg) 17 | # calculate derivative of the velocity 18 | dv_dt = (1.0/(m+load)) * (Fp*u - 0.5*rho*Cd*A*v**2) 19 | return dv_dt 20 | 21 | tf = 60.0 # final time for simulation 22 | nsteps = 61 # number of time steps 23 | delta_t = tf/(nsteps-1) # how long is each time step? 24 | ts = np.linspace(0,tf,nsteps) # linearly spaced time vector 25 | 26 | # simulate step test operation 27 | step = np.zeros(nsteps) # u = valve % open 28 | step[11:] = 50.0 # step up pedal position 29 | # passenger(s) + cargo load 30 | load = 200.0 # kg 31 | # velocity initial condition 32 | v0 = 0.0 33 | # for storing the results 34 | vs = np.zeros(nsteps) 35 | 36 | # simulate with ODEINT 37 | for i in range(nsteps-1): 38 | u = step[i] 39 | v = odeint(vehicle,v0,[0,delta_t],args=(u,load)) 40 | v0 = v[-1] # take the last value as initial condition 41 | vs[i+1] = v0 # store the velocity for plotting 42 | 43 | # plot results 44 | plt.figure() 45 | plt.subplot(2,1,1) 46 | plt.plot(ts,vs,'b-',linewidth=3) 47 | plt.ylabel('Velocity (m/s)') 48 | plt.legend(['Velocity'],loc=2) 49 | plt.subplot(2,1,2) 50 | plt.plot(ts,step,'r--',linewidth=3) 51 | plt.ylabel('Gas Pedal') 52 | plt.legend(['Gas Pedal (%)']) 53 | plt.xlabel('Time (sec)') 54 | plt.show() -------------------------------------------------------------------------------- /Module_27/README.md: -------------------------------------------------------------------------------- 1 | ## Module 27 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [Second Order Systems](https://www.apmonitor.com/pdc/index.php/Main/SecondOrderSystems): 1 scripts. Description: A second-order linear system is a common description of many dynamic processes. Overdamped, critically damped, and underdamped second order systems are three classes. 6 | ### Quiz 7 | - [Quiz on Second Order Graphical Methods](https://www.apmonitor.com/pdc/index.php/Main/QuizSecondOrderSystems): 0 scripts. Description: Learning assessment on second order dynamic system graphical methods. 8 | ### Assignment 9 | - [Graphical Method: Second Order Underdamped](https://www.apmonitor.com/pdc/index.php/Main/SecondOrderApplications): 1 scripts. Description: Transfer functions (Laplace domain) help analyze dynamic systems. Exercises are to transform a linear differential equation into the Laplace domain and reposition the variables to create a transfer function. 10 | ### TCLab 11 | - [TCLab On/Off Control](https://www.apmonitor.com/pdc/index.php/Main/TCLabOnOffControl): 3 scripts. Description: On/off control is used in most cooling and heating applications where the actuator can only be in an On of Off state. The temperature control lab shows the application of On/Off control and fits a 2nd order model to the closed-loop response. 12 | ## Module 27 in Process Dynamics and Control 13 | - [Course Overview](https://apmonitor.com/pdc) 14 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 15 | ### Topic 16 | - [Graphically Fit Second Order Response](https://www.apmonitor.com/pdc/index.php/Main/SecondOrderGraphical): 1 scripts. Description: An oscillating second-order linear system describes many underdamped dynamic processes. 17 | -------------------------------------------------------------------------------- /Module_12/4_TCLab/TCLabPIControl_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | 6 | # ----------------------------- 7 | # Input controller gain (Kc) 8 | # Input controller time constant (tauI) 9 | # ----------------------------- 10 | Kc = 11 | tauI = 12 | 13 | n = 600 # Number of second time points (10 min) 14 | tm = np.linspace(0,n-1,n) # Time values 15 | lab = tclab.TCLab() 16 | T1 = np.zeros(n) 17 | Q1 = np.zeros(n) 18 | # step setpoint from 23.0 to 60.0 degC 19 | SP1 = np.ones(n)*23.0 20 | SP1[10:] = 60.0 21 | Q1_bias = 0.0 22 | for i in range(n): 23 | # record measurement 24 | T1[i] = lab.T1 25 | 26 | # -------------------------------------------------- 27 | # fill-in PI controller equation to change Q1[i] 28 | # -------------------------------------------------- 29 | Q1[i] = 30 | 31 | # -------------------------------------------------- 32 | # implement new heater value with integral anti-reset windup 33 | Q1[i] = max(0,min(100,Q1[i])) # clip to 0-100% 34 | # -------------------------------------------------- 35 | 36 | lab.Q1(Q1[i]) 37 | if i%20==0: 38 | print(' Heater, Temp, Setpoint') 39 | print(f'{Q1[i]:7.2f},{T1[i]:7.2f},{SP1[i]:7.2f}') 40 | # wait for 1 sec 41 | time.sleep(1) 42 | lab.close() 43 | # Save data file 44 | data = np.vstack((tm,Q1,T1,SP1)).T 45 | np.savetxt('PI_control.csv',data,delimiter=',',\ 46 | header='Time,Q1,T1,SP1',comments='') 47 | 48 | # Create Figure 49 | plt.figure(figsize=(10,7)) 50 | ax = plt.subplot(2,1,1) 51 | ax.grid() 52 | plt.plot(tm/60.0,SP1,'k-',label=r'$T_1$ SP') 53 | plt.plot(tm/60.0,T1,'r.',label=r'$T_1$ PV') 54 | plt.ylabel(r'Temp ($^oC$)') 55 | plt.legend(loc=2) 56 | ax = plt.subplot(2,1,2) 57 | ax.grid() 58 | plt.plot(tm/60.0,Q1,'b-',label=r'$Q_1$') 59 | plt.ylabel(r'Heater (%)') 60 | plt.xlabel('Time (min)') 61 | plt.legend(loc=1) 62 | plt.savefig('PI_Control.png') 63 | plt.show() -------------------------------------------------------------------------------- /Module_06/2_Quiz/QuizFirstOrderGraphical_2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.integrate import odeint 4 | from scipy.interpolate import interp1d 5 | 6 | # define first-order plus dead-time approximation 7 | def fopdt(y,t,uf,Km,taum,thetam): 8 | # arguments 9 | # y = output 10 | # t = time 11 | # uf = input linear function (for time shift) 12 | # Km = model gain 13 | # taum = model time constant 14 | # thetam = model time constant 15 | # time-shift u 16 | try: 17 | if (t-thetam) <= 0: 18 | um = uf(0.0) 19 | else: 20 | um = uf(t-thetam) 21 | except: 22 | um = 0 23 | # calculate derivative 24 | dydt = (-y + Km * um)/taum 25 | return dydt 26 | 27 | # specify number of steps 28 | ns = 150 29 | # define time points 30 | t = np.linspace(0,15,ns+1) 31 | delta_t = t[1]-t[0] 32 | # define input vector 33 | u = np.zeros(ns+1) 34 | u[10:] = -2.0 35 | # create linear interpolation of the u data versus time 36 | uf = interp1d(t,u) 37 | 38 | # simulate FOPDT model with x=[Km,taum,thetam] 39 | def sim_model(Km,taum,thetam): 40 | # input arguments 41 | #Km 42 | #taum 43 | #thetam 44 | # storage for model values 45 | ym = np.zeros(ns+1) # model 46 | # initial condition 47 | ym[0] = 0 48 | # loop through time steps 49 | for i in range(1,ns+1): 50 | ts = [delta_t*(i-1),delta_t*i] 51 | y1 = odeint(fopdt,ym[i-1],ts,args=(uf,Km,taum,thetam)) 52 | ym[i] = y1[-1] 53 | return ym 54 | 55 | # calculate model with updated parameters 56 | Km = 2.0 57 | taum = 2.0 58 | thetam = 2.0 59 | ym = sim_model(Km,taum,thetam) 60 | 61 | # plot results 62 | plt.figure() 63 | plt.subplot(2,1,1) 64 | plt.plot(t,u,'b-',linewidth=2) 65 | plt.legend(['u'],loc='best') 66 | plt.ylabel('Input Step (u)') 67 | plt.grid() 68 | plt.subplot(2,1,2) 69 | plt.plot(t,ym,'k--',linewidth=2,label='y') 70 | plt.ylabel('Output Response (y)') 71 | plt.legend(loc='best') 72 | plt.xlabel('Time (sec)') 73 | plt.grid() 74 | plt.show() -------------------------------------------------------------------------------- /Module_07/1_Topic/FirstOrderOptimization_1.py: -------------------------------------------------------------------------------- 1 | # Generate process data as data.txt 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | from scipy.integrate import odeint 5 | 6 | # define process model (to generate process data) 7 | def process(y,t,n,u,Kp,taup): 8 | # arguments 9 | # y[n] = outputs 10 | # t = time 11 | # n = order of the system 12 | # u = input value 13 | # Kp = process gain 14 | # taup = process time constant 15 | 16 | # equations for higher order system 17 | dydt = np.zeros(n) 18 | # calculate derivative 19 | dydt[0] = (-y[0] + Kp * u)/(taup/n) 20 | for i in range(1,n): 21 | dydt[i] = (-y[i] + y[i-1])/(taup/n) 22 | return dydt 23 | 24 | # specify number of steps 25 | ns = 50 26 | # define time points 27 | t = np.linspace(0,40,ns+1) 28 | delta_t = t[1]-t[0] 29 | # define input vector 30 | u = np.zeros(ns+1) 31 | u[5:20] = 1.0 32 | u[20:30] = 0.1 33 | u[30:] = 0.5 34 | 35 | # use this function or replace yp with real process data 36 | def sim_process_data(): 37 | # higher order process 38 | n=10 # order 39 | Kp=3.0 # gain 40 | taup=5.0 # time constant 41 | # storage for predictions or data 42 | yp = np.zeros(ns+1) # process 43 | for i in range(1,ns+1): 44 | if i==1: 45 | yp0 = np.zeros(n) 46 | ts = [delta_t*(i-1),delta_t*i] 47 | y = odeint(process,yp0,ts,args=(n,u[i],Kp,taup)) 48 | yp0 = y[-1] 49 | yp[i] = y[1][n-1] 50 | return yp 51 | yp = sim_process_data() 52 | 53 | # Construct results and save data file 54 | # Column 1 = time 55 | # Column 2 = input 56 | # Column 3 = output 57 | data = np.vstack((t,u,yp)) # vertical stack 58 | data = data.T # transpose data 59 | np.savetxt('data.txt',data,delimiter=',') 60 | 61 | # plot results 62 | plt.figure() 63 | plt.subplot(2,1,1) 64 | plt.plot(t,yp,'kx-',linewidth=2,label='Output') 65 | plt.ylabel('Output Data') 66 | plt.legend(loc='best') 67 | plt.subplot(2,1,2) 68 | plt.plot(t,u,'bx-',linewidth=2) 69 | plt.legend(['Input'],loc='best') 70 | plt.ylabel('Input Data') 71 | plt.show() -------------------------------------------------------------------------------- /Module_28/1_Topic/SecondOrderOptimization_1.py: -------------------------------------------------------------------------------- 1 | # Generate process data as data.txt 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | from scipy.integrate import odeint 5 | 6 | # define process model (to generate process data) 7 | def process(y,t,n,u,Kp,taup): 8 | # arguments 9 | # y[n] = outputs 10 | # t = time 11 | # n = order of the system 12 | # u = input value 13 | # Kp = process gain 14 | # taup = process time constant 15 | 16 | # equations for higher order system 17 | dydt = np.zeros(n) 18 | # calculate derivative 19 | dydt[0] = (-y[0] + Kp * u)/(taup/n) 20 | for i in range(1,n): 21 | dydt[i] = (-y[i] + y[i-1])/(taup/n) 22 | return dydt 23 | 24 | # specify number of steps 25 | ns = 50 26 | # define time points 27 | t = np.linspace(0,40,ns+1) 28 | delta_t = t[1]-t[0] 29 | # define input vector 30 | u = np.zeros(ns+1) 31 | u[5:20] = 1.0 32 | u[20:30] = 0.1 33 | u[30:] = 0.5 34 | 35 | # use this function or replace yp with real process data 36 | def sim_process_data(): 37 | # higher order process 38 | n=10 # order 39 | Kp=3.0 # gain 40 | taup=5.0 # time constant 41 | # storage for predictions or data 42 | yp = np.zeros(ns+1) # process 43 | for i in range(1,ns+1): 44 | if i==1: 45 | yp0 = np.zeros(n) 46 | ts = [delta_t*(i-1),delta_t*i] 47 | y = odeint(process,yp0,ts,args=(n,u[i],Kp,taup)) 48 | yp0 = y[-1] 49 | yp[i] = y[1][n-1] 50 | return yp 51 | yp = sim_process_data() 52 | 53 | # Construct results and save data file 54 | # Column 1 = time 55 | # Column 2 = input 56 | # Column 3 = output 57 | data = np.vstack((t,u,yp)) # vertical stack 58 | data = data.T # transpose data 59 | np.savetxt('data.txt',data,delimiter=',') 60 | 61 | # plot results 62 | plt.figure() 63 | plt.subplot(2,1,1) 64 | plt.plot(t,yp,'kx-',linewidth=2,label='Output') 65 | plt.ylabel('Output Data') 66 | plt.legend(loc='best') 67 | plt.subplot(2,1,2) 68 | plt.plot(t,u,'bx-',linewidth=2) 69 | plt.legend(['Input'],loc='best') 70 | plt.ylabel('Input Data') 71 | plt.show() -------------------------------------------------------------------------------- /Module_12/4_TCLab/TCLabPIControl_2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | 6 | # ----------------------------- 7 | # Input controller gain (Kc) 8 | # Input controller time constant (tauI) 9 | # ----------------------------- 10 | Kc = 1/0.9 11 | tauI = 175.0 12 | 13 | n = 600 # Number of second time points (10 min) 14 | tm = np.linspace(0,n-1,n) # Time values 15 | lab = tclab.TCLab() 16 | T1 = np.zeros(n) 17 | Q1 = np.zeros(n) 18 | # step setpoint from 23.0 to 60.0 degC 19 | SP1 = np.ones(n)*23.0 20 | SP1[10:] = 60.0 21 | Q1_bias = 0.0 22 | ierr = 0.0 23 | for i in range(n): 24 | # record measurement 25 | T1[i] = lab.T1 26 | 27 | # -------------------------------------------------- 28 | # fill-in PI controller equation to change Q1[i] 29 | # -------------------------------------------------- 30 | err = SP1[i]-T1[i] 31 | ierr += err 32 | Q1[i] = Q1_bias + Kc*err + Kc/tauI * ierr 33 | 34 | # -------------------------------------------------- 35 | # implement new heater value with integral anti-reset windup 36 | if Q1[i]>=100: 37 | Q1[i]=100 38 | ierr -= err 39 | if Q1[i]<=0: 40 | Q1[i]=0 41 | ierr -= err 42 | # -------------------------------------------------- 43 | 44 | lab.Q1(Q1[i]) 45 | if i%20==0: 46 | print(' Heater, Temp, Setpoint') 47 | print(f'{Q1[i]:7.2f},{T1[i]:7.2f},{SP1[i]:7.2f}') 48 | # wait for 1 sec 49 | time.sleep(1) 50 | lab.close() 51 | # Save data file 52 | data = np.vstack((tm,Q1,T1,SP1)).T 53 | np.savetxt('PI_control.csv',data,delimiter=',',\ 54 | header='Time,Q1,T1,SP1',comments='') 55 | 56 | # Create Figure 57 | plt.figure(figsize=(10,7)) 58 | ax = plt.subplot(2,1,1) 59 | ax.grid() 60 | plt.plot(tm/60.0,SP1,'k-',label=r'$T_1$ SP') 61 | plt.plot(tm/60.0,T1,'r.',label=r'$T_1$ PV') 62 | plt.ylabel(r'Temp ($^oC$)') 63 | plt.legend(loc=2) 64 | ax = plt.subplot(2,1,2) 65 | ax.grid() 66 | plt.plot(tm/60.0,Q1,'b-',label=r'$Q_1$') 67 | plt.ylabel(r'Heater (%)') 68 | plt.xlabel('Time (min)') 69 | plt.legend(loc=1) 70 | plt.savefig('PI_Control.png') 71 | plt.show() -------------------------------------------------------------------------------- /Module_05/3_Assignment/LinearizeDiffEqns_4.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.integrate import odeint 4 | 5 | # define thermocouple model 6 | def thermocouple(x,t,Tg,Tf): 7 | ## States 8 | Tt = x[0] 9 | Tlin = x[1] 10 | 11 | ## Parameters 12 | h = 2800.0 # W/m^2-K 13 | rho = 20.0 # gm/cm^3 14 | sigma = 5.67e-8 # W/m^2-K^4 15 | eps = 0.8 # 16 | Cp = 0.4 # J/gm-K 17 | d = 0.01 # cm 18 | r = d / 2.0 # radius 19 | A = 4.0 * np.pi * (r/100.0)**2 # sphere area (m^2) 20 | V = 4.0/3.0 * np.pi * r**3 # sphere volume (cm^3) 21 | 22 | # acc = inlet - outlet 23 | # acc = m * Cp * dT/dt = rho * V * Cp * dT/dt 24 | # inlet - outlet from 2 heat transfer pathways 25 | # q(convection) = h * A * (Tg-Tt) 26 | # q(radiation) = A * esp * sigma * (Tf^4 - Tt^4) 27 | q_conv = h * A * (Tg-Tt) 28 | q_rad = A * eps * sigma * (Tf**4 - Tt**4) 29 | dTdt = (q_conv + q_rad) / (rho * V * Cp) 30 | dTlin_dt = 0.0 # add linearized equation 31 | return [dTdt,dTlin_dt] 32 | 33 | # Flame temperature 34 | Tf0 = 1500.0 #K 35 | 36 | # Starting thermocouple temperature 37 | y0 = [Tf0,Tf0] 38 | 39 | # Time Intervals (sec) 40 | t = np.linspace(0,0.1,1000) 41 | 42 | # Flame Temperature 43 | Tf = np.ones(len(t))*Tf0 44 | Tf[500:] = 1520.0 #K 45 | 46 | # Gas temperature cycles 47 | Tg = Tf + (150.0/2.0) * np.sin(2.0 * np.pi * 100.0 * t) #K 48 | 49 | # Store thermocouple temperature for plotting 50 | Tt = np.ones(len(t)) * Tf 51 | Tlin = np.ones(len(t)) * Tf 52 | 53 | for i in range(len(t)-1): 54 | ts = [t[i],t[i+1]] 55 | y = odeint(thermocouple,y0,ts,args=(Tg[i],Tf[i])) 56 | y0 = y[-1] 57 | Tt[i+1] = y0[0] 58 | Tlin[i+1] = y0[1] 59 | 60 | # Plot the results 61 | plt.figure() 62 | plt.plot(t,Tg,'b--',linewidth=3,label='Gas Temperature') 63 | plt.plot(t,Tf,'g:',linewidth=3,label='Flame Temperature') 64 | plt.plot(t,Tt,'r-',linewidth=3,label='Thermocouple') 65 | plt.ylabel('Temperature (K)') 66 | plt.legend(loc='best') 67 | plt.xlim([0,t[-1]]) 68 | plt.xlabel('Time (sec)') 69 | 70 | plt.show() -------------------------------------------------------------------------------- /Module_11/4_TCLab/TCLabPOnlyControl_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | 6 | # ----------------------------- 7 | # Adjust controller gain (Kc) 8 | # from ITAE tuning correlation 9 | # ----------------------------- 10 | Kc = 4.45 11 | 12 | n = 600 # Number of second time points (10 min) 13 | tm = np.linspace(0,n-1,n) # Time values 14 | lab = tclab.TCLab() 15 | T1 = np.zeros(n) 16 | Q1 = np.zeros(n) 17 | # step setpoint from 23.0 to 60.0 degC 18 | SP1 = np.ones(n)*23.0 19 | SP1[10:] = 60.0 20 | Q1_bias = 0.0 21 | for i in range(n): 22 | # record measurement 23 | T1[i] = lab.T1 24 | 25 | # -------------------------------------------------- 26 | # fill-in P-only controller equation to change Q1[i] 27 | # -------------------------------------------------- 28 | Q1[i] = Q1_bias + Kc * (SP1[i]-T1[i]) 29 | 30 | # implement new heater value 31 | Q1[i] = max(0,min(100,Q1[i])) # clip to 0-100% 32 | lab.Q1(Q1[i]) 33 | if i%20==0: 34 | print(' Heater, Temp, Setpoint') 35 | print(f'{Q1[i]:7.2f},{T1[i]:7.2f},{SP1[i]:7.2f}') 36 | # wait for 1 sec 37 | time.sleep(1) 38 | lab.close() 39 | # Save data file 40 | data = np.vstack((tm,Q1,T1,SP1)).T 41 | np.savetxt('P-only.csv',data,delimiter=',',\ 42 | header='Time,Q1,T1,SP1',comments='') 43 | 44 | # Create Figure 45 | plt.figure(figsize=(10,7)) 46 | ax = plt.subplot(2,1,1) 47 | ax.grid() 48 | plt.plot(tm/60.0,SP1,'k-',label=r'$T_1$ SP') 49 | plt.plot(tm/60.0,T1,'r.',label=r'$T_1$ PV') 50 | plt.text(6.1,30,'Measured Offset: ' + str(np.round(SP1[-1]-T1[-1],2))) 51 | offset = 60 - (23+0.9*Kc*60)/(1+0.9*Kc) 52 | plt.text(6.1,26,'Calculated Offset: ' + str(np.round(offset,2))) 53 | plt.text(6.1,22,r'$K_c$: ' + str(np.round(Kc,2))) 54 | plt.plot([tm[-1]/60.0,tm[-1]/60.0],[SP1[-1],T1[-1]],\ 55 | 'b-',lw=3,alpha=0.5) 56 | plt.ylabel(r'Temp ($^oC$)') 57 | plt.xlim([0,10]) 58 | plt.legend(loc=2) 59 | ax = plt.subplot(2,1,2) 60 | ax.grid() 61 | plt.plot(tm/60.0,Q1,'b-',label=r'$Q_1$') 62 | plt.ylabel(r'Heater (%)') 63 | plt.xlabel('Time (min)') 64 | plt.legend(loc=1) 65 | plt.savefig('P-only_Control.png') 66 | plt.show() -------------------------------------------------------------------------------- /Module_06/README.md: -------------------------------------------------------------------------------- 1 | ## Module 6 in Process Dynamics and Control 2 | - [Course Overview](https://apmonitor.com/pdc) 3 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 4 | ### Topic 5 | - [First Order Plus Dead Time (FOPDT)](https://www.apmonitor.com/pdc/index.php/Main/FirstOrderSystems): 0 scripts. Description: A first-order linear system with time delay is a common empirical description of many dynamic processes. 6 | ### Quiz 7 | - [Quiz: FOPDT Graphical Fit](https://www.apmonitor.com/pdc/index.php/Main/QuizFirstOrderGraphical): 2 scripts. Description: Learning assessment on obtaining parameters for a first order linear differential equation with a graphical method. 8 | ### Assignment 9 | - [Graphical Method: FOPDT to Step Test](https://www.apmonitor.com/pdc/index.php/Main/FirstOrderFit): 0 scripts. Description: A first-order linear system with time delay is a common empirical description of many dynamic processes. Graphical methods are used to fit a first order system to process data. 10 | ### TCLab 11 | - [TCLab FOPDT Model](https://www.apmonitor.com/pdc/index.php/Main/TCLabFOPDT): 1 scripts. Description: Linear Dynamic Temperature Response Model of a Heater and Temperature Sensor with an Arduino 12 | ## Module 6 in Process Dynamics and Control 13 | - [Course Overview](https://apmonitor.com/pdc) 14 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 15 | ### Topic 16 | - [Time Delay in Dynamic Systems](https://www.apmonitor.com/pdc/index.php/Main/FirstOrderPlusDeadTime): 1 scripts. Description: Time delay in a first-order linear system is a shift in the effect of an input on the output response. 17 | ## Module 6 in Process Dynamics and Control 18 | - [Course Overview](https://apmonitor.com/pdc) 19 | - [Course Schedule](https://apmonitor.com/pdc/index.php/Main/CourseSchedule) 20 | ### Topic 21 | - [Graphical Method: FOPDT to Step Test](https://www.apmonitor.com/pdc/index.php/Main/FirstOrderGraphical): 2 scripts. Description: A first-order linear system with time delay is a common empirical description of many dynamic processes. This Python source code demonstrates how to simulate a step test and compare with an FOPDT approximation. 22 | -------------------------------------------------------------------------------- /Module_36/1_Topic/LinearProgramming_6.py: -------------------------------------------------------------------------------- 1 | from gekko import GEKKO 2 | 3 | m = GEKKO() 4 | 5 | # variables 6 | x1 = m.Var(value=0 , lb=0 , ub=5 , name='x1') # Product 1 7 | x2 = m.Var(value=0 , lb=0 , ub=4 , name='x2') # Product 2 8 | profit = m.Var(value=1 , name='profit') 9 | 10 | # profit function 11 | m.Obj(-profit) 12 | m.Equation(profit==100*x1+125*x2) 13 | m.Equation(3*x1+6*x2<=30) 14 | m.Equation(8*x1+4*x2<=44) 15 | 16 | m.solve() 17 | 18 | print ('') 19 | print ('--- Results of the Optimization Problem ---') 20 | print ('Product 1 (x1): ' + str(x1[0])) 21 | print ('Product 2 (x2): ' + str(x2[0])) 22 | print ('Profit: ' + str(profit[0])) 23 | 24 | ## Generate a contour plot 25 | # Import some other libraries that we'll need 26 | # matplotlib and numpy packages must also be installed 27 | import matplotlib 28 | import numpy as np 29 | import matplotlib.pyplot as plt 30 | 31 | # Design variables at mesh points 32 | x = np.arange(-1.0, 8.0, 0.02) 33 | y = np.arange(-1.0, 6.0, 0.02) 34 | x1, x2 = np.meshgrid(x,y) 35 | 36 | # Equations and Constraints 37 | profit = 100.0 * x1 + 125.0 * x2 38 | A_usage = 3.0 * x1 + 6.0 * x2 39 | B_usage = 8.0 * x1 + 4.0 * x2 40 | 41 | # Create a contour plot 42 | plt.figure() 43 | # Weight contours 44 | lines = np.linspace(100.0,800.0,8) 45 | CS = plt.contour(x1,x2,profit,lines,colors='g') 46 | plt.clabel(CS, inline=1, fontsize=10) 47 | # A usage < 30 48 | CS = plt.contour(x1,x2,A_usage,[26.0, 28.0, 30.0],colors='r',linewidths=[0.5,1.0,4.0]) 49 | plt.clabel(CS, inline=1, fontsize=10) 50 | # B usage < 44 51 | CS = plt.contour(x1, x2,B_usage,[40.0,42.0,44.0],colors='b',linewidths=[0.5,1.0,4.0]) 52 | plt.clabel(CS, inline=1, fontsize=10) 53 | # Container for 0 <= Product 1 <= 500 L 54 | CS = plt.contour(x1, x2,x1 ,[0.0, 0.1, 4.9, 5.0],colors='k',linewidths=[4.0,1.0,1.0,4.0]) 55 | plt.clabel(CS, inline=1, fontsize=10) 56 | # Container for 0 <= Product 2 <= 400 L 57 | CS = plt.contour(x1, x2,x2 ,[0.0, 0.1, 3.9, 4.0],colors='k',linewidths=[4.0,1.0,1.0,4.0]) 58 | plt.clabel(CS, inline=1, fontsize=10) 59 | 60 | # Add some labels 61 | plt.title('Soft Drink Production Problem') 62 | plt.xlabel('Product 1 (100 L)') 63 | plt.ylabel('Product 2 (100 L)') 64 | # Save the figure as a PNG 65 | plt.savefig('contour.png') 66 | 67 | # Show the plots 68 | plt.show() -------------------------------------------------------------------------------- /Module_13/1_Topic/ProportionalIntegralDerivative_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.integrate import odeint 4 | 5 | # process model 6 | Kp = 3.0 7 | taup = 5.0 8 | def process(y,t,u,Kp,taup): 9 | # Kp = process gain 10 | # taup = process time constant 11 | dydt = -y/taup + Kp/taup * u 12 | return dydt 13 | 14 | # specify number of steps 15 | ns = 300 16 | # define time points 17 | t = np.linspace(0,ns/10,ns+1) 18 | delta_t = t[1]-t[0] 19 | 20 | # storage for recording values 21 | op = np.zeros(ns+1) # controller output 22 | pv = np.zeros(ns+1) # process variable 23 | e = np.zeros(ns+1) # error 24 | ie = np.zeros(ns+1) # integral of the error 25 | dpv = np.zeros(ns+1) # derivative of the pv 26 | P = np.zeros(ns+1) # proportional 27 | I = np.zeros(ns+1) # integral 28 | D = np.zeros(ns+1) # derivative 29 | sp = np.zeros(ns+1) # set point 30 | sp[25:] = 10 31 | 32 | # PID (starting point) 33 | Kc = 1.0/Kp 34 | tauI = taup 35 | tauD = 0.0 36 | 37 | # PID (tuning) 38 | Kc = Kc * 2 39 | tauI = tauI / 2 40 | tauD = 1.0 41 | 42 | # Upper and Lower limits on OP 43 | op_hi = 10.0 44 | op_lo = 0.0 45 | 46 | # loop through time steps 47 | for i in range(0,ns): 48 | e[i] = sp[i] - pv[i] 49 | if i >= 1: # calculate starting on second cycle 50 | dpv[i] = (pv[i]-pv[i-1])/delta_t 51 | ie[i] = ie[i-1] + e[i] * delta_t 52 | P[i] = Kc * e[i] 53 | I[i] = Kc/tauI * ie[i] 54 | D[i] = - Kc * tauD * dpv[i] 55 | op[i] = op[0] + P[i] + I[i] + D[i] 56 | if op[i] > op_hi: # check upper limit 57 | op[i] = op_hi 58 | ie[i] = ie[i] - e[i] * delta_t # anti-reset windup 59 | if op[i] < op_lo: # check lower limit 60 | op[i] = op_lo 61 | ie[i] = ie[i] - e[i] * delta_t # anti-reset windup 62 | y = odeint(process,pv[i],[0,delta_t],args=(op[i],Kp,taup)) 63 | pv[i+1] = y[-1] 64 | op[ns] = op[ns-1] 65 | ie[ns] = ie[ns-1] 66 | P[ns] = P[ns-1] 67 | I[ns] = I[ns-1] 68 | D[ns] = D[ns-1] 69 | 70 | # plot results 71 | plt.figure(1) 72 | 73 | plt.plot(t,sp,'k-',linewidth=2) 74 | plt.plot(t,pv,'b--',linewidth=3) 75 | plt.legend(['Set Point (SP)','Process Variable (PV)'],loc='best') 76 | plt.ylabel('Process') 77 | plt.ylim([-0.1,12]) 78 | plt.xlabel('Time') 79 | plt.show() -------------------------------------------------------------------------------- /Module_25/4_TCLab/TCLabBlockDiagram_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import tclab 4 | import time 5 | 6 | n = 600 # Number of second time points (10 min) 7 | tm = np.linspace(0,n-1,n) # Time values 8 | T1Pred = # fill in analytic solution 9 | 10 | # Kc,tauI,tauD 11 | Kc = 2.0 12 | tauI = 180.0 13 | tauD = 0 14 | 15 | # PID Controller Function 16 | def pid(sp,pv,pv_last,ierr,dt): 17 | # Parameters in terms of PID coefficients 18 | KP = Kc 19 | KI = Kc/tauI 20 | KD = Kc*tauD 21 | # ubias for controller (initial heater) 22 | op0 = 0 23 | # upper and lower bounds on heater level 24 | ophi = 100 25 | oplo = 0 26 | # calculate the error 27 | error = sp-pv 28 | # calculate the integral error 29 | ierr = ierr + KI * error * dt 30 | # calculate the measurement derivative 31 | dpv = (pv - pv_last) / dt 32 | # calculate the PID output 33 | P = KP * error 34 | I = ierr 35 | D = -KD * dpv 36 | op = op0 + P + I + D 37 | # implement anti-reset windup 38 | if op < oplo or op > ophi: 39 | I = I - KI * error * dt 40 | # clip output 41 | op = max(oplo,min(ophi,op)) 42 | # return the controller output and PID terms 43 | return [op,P,I,D] 44 | 45 | lab = tclab.TCLab() 46 | T1 = np.zeros(n) 47 | Q1 = np.zeros(n) 48 | # step setpoint from 23.0 to 43.0 degC 49 | SP1 = np.ones(n)*23.0 50 | SP1[1:] = 43.0 51 | Q1_bias = 0.0 52 | ierr = 0.0 53 | for i in range(n): 54 | T1[i] = lab.T1 55 | [Q1[i],P,ierr,D] = pid(SP1[i],T1[i],T1[max(0,i-1)],ierr,1.0) 56 | lab.Q1(Q1[i]) 57 | if i%20==0: 58 | print(' Heater, Temp, Setpoint') 59 | print(f'{Q1[i]:7.2f},{T1[i]:7.2f},{SP1[i]:7.2f}') 60 | time.sleep(1) 61 | lab.close() 62 | 63 | # Save data file 64 | data = np.vstack((tm,Q1,T1,SP1)).T 65 | np.savetxt('PI_control.csv',data,delimiter=',',\ 66 | header='Time,Q1,T1,SP1',comments='') 67 | 68 | # Create Figure 69 | plt.figure(figsize=(10,7)) 70 | ax = plt.subplot(2,1,1) 71 | ax.grid() 72 | plt.plot(tm/60.0,SP1,'k-',label=r'$T_1$ SP') 73 | plt.plot(tm/60.0,T1,'r.',label=r'$T_1$ PV') 74 | plt.plot(tm/60.0,T1Pred,'b-',label=r'$T_1$ Predicted') 75 | plt.ylabel(r'Temp ($^oC$)') 76 | plt.legend(loc=2) 77 | ax = plt.subplot(2,1,2) 78 | ax.grid() 79 | plt.plot(tm/60.0,Q1,'b-',label=r'$Q_1$') 80 | plt.ylabel(r'Heater (%)') 81 | plt.xlabel('Time (min)') 82 | plt.legend(loc=1) 83 | plt.savefig('PI_Control.png') 84 | plt.show() -------------------------------------------------------------------------------- /Module_27/1_Topic/SecondOrderSystems_1.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from scipy.integrate import odeint 4 | 5 | # specify number of steps 6 | ns = 100 7 | # define time points 8 | t = np.linspace(0,ns/5.0,ns+1) 9 | 10 | class model(object): 11 | # default process model 12 | Kp = 2.0 13 | taus = 1.0 14 | thetap = 0.0 15 | zeta = 1.0 16 | 17 | def process(x,t,u,Kp,taus,zeta): 18 | # Kp = process gain 19 | # taus = second order time constant 20 | # zeta = damping factor 21 | # ts^2 dy2/dt2 + 2 zeta taus dydt + y = Kp u(t-thetap) 22 | y = x[0] 23 | dydt = x[1] 24 | dy2dt2 = (-2.0*zeta*taus*dydt - y + Kp*u)/taus**2 25 | return [dydt,dy2dt2] 26 | 27 | def calc_response(t,m): 28 | # t = time points 29 | # m = process model 30 | Kp = m.Kp 31 | taus = m.taus 32 | thetap = m.thetap 33 | zeta = m.zeta 34 | 35 | print('Kp: ' + str(Kp)) 36 | print('taus: ' + str(taus)) 37 | print('zeta: ' + str(zeta)) 38 | 39 | # specify number of steps 40 | ns = len(t)-1 41 | delta_t = t[1]-t[0] 42 | 43 | # storage for recording values 44 | op = np.zeros(ns+1) # controller output 45 | pv = np.zeros((ns+1,2)) # process variable 46 | 47 | # step input 48 | op[5:]=1.0 49 | 50 | # Simulate time delay 51 | ndelay = int(np.ceil(thetap / delta_t)) 52 | 53 | # loop through time steps 54 | for i in range(0,ns): 55 | # implement time delay 56 | iop = max(0,i-ndelay) 57 | inputs = (op[iop],Kp,taus,zeta) 58 | y = odeint(process,pv[i],[0,delta_t],args=inputs) 59 | pv[i+1] = y[-1] 60 | return (pv,op) 61 | 62 | # overdamped step response 63 | model.zeta = 2.0 64 | (pv1,op1) = calc_response(t,model) 65 | 66 | # critically damped step response 67 | model.zeta = 1.0 68 | (pv2,op2) = calc_response(t,model) 69 | 70 | # underdamped step response 71 | model.zeta = 0.5 72 | (pv3,op3) = calc_response(t,model) 73 | 74 | plt.figure(1) 75 | 76 | plt.subplot(2,1,1) 77 | plt.plot(t,pv3[:,0],'b-',linewidth=3,label='Underdamped') 78 | plt.plot(t,pv2[:,0],'k:',linewidth=3,label='Critically Damped') 79 | plt.plot(t,pv1[:,0],'r--',linewidth=3,label='Overdamped') 80 | plt.legend(loc='best') 81 | plt.ylabel('Process Output') 82 | 83 | plt.subplot(2,1,2) 84 | plt.plot(t,op1,'r-',linewidth=3,label='Step Input') 85 | plt.legend(loc='best') 86 | plt.ylabel('Process Input') 87 | 88 | plt.xlabel('Time') 89 | 90 | plt.savefig('output.png') 91 | plt.show() -------------------------------------------------------------------------------- /Module_22/4_TCLab/ArduinoEstimation2_8.py: -------------------------------------------------------------------------------- 1 | close all; clear all; clc 2 | 3 | global t T1meas T2meas Q1 Q2 4 | 5 | % generate data file from TCLab or get sample data file from: 6 | % https://apmonitor.com/pdc/index.php/Main/ArduinoEstimation2 7 | % Import data file 8 | % Column 1 = time (t) 9 | % Column 2 = input (u) 10 | % Column 3 = output (yp) 11 | filename = 'data.txt'; 12 | delimiterIn = ','; 13 | headerlinesIn = 1; 14 | z = importdata(filename,delimiterIn,headerlinesIn); 15 | % extract data columns 16 | t = z.data(:,1); 17 | Q1 = z.data(:,2); 18 | Q2 = z.data(:,3); 19 | T1meas = z.data(:,4); 20 | T2meas = z.data(:,5); 21 | 22 | % number of time points 23 | ns = length(t); 24 | 25 | % Parameter initial guess 26 | U = 10.0; % Heat transfer coefficient (W/m^2-K) 27 | alpha1 = 0.0100; % Heat gain 1 (W/%) 28 | alpha2 = 0.0075; % Heat gain 2 (W/%) 29 | p0 = [U,alpha1,alpha2]; 30 | 31 | % show initial objective 32 | disp(['Initial SSE Objective: ' num2str(objective(p0))]) 33 | 34 | % optimize parameters 35 | 36 | % no linear constraints 37 | A = []; 38 | b = []; 39 | Aeq = []; 40 | beq = []; 41 | nlcon = []; 42 | 43 | % optimize with fmincon 44 | lb = [2,0.005,0.002]; % lower bound 45 | ub = [20,0.02,0.015]; % upper bound 46 | % options = optimoptions(@fmincon,'Algorithm','interior-point'); 47 | p = fmincon(@objective,p0,A,b,Aeq,beq,lb,ub,nlcon); %,options); 48 | 49 | % show final objective 50 | disp(['Final SSE Objective: ' num2str(objective(p))]) 51 | 52 | % optimized parameter values 53 | U = p(1); 54 | alpha1 = p(2); 55 | alpha2 = p(3); 56 | disp(['U: ' num2str(U)]) 57 | disp(['alpha1: ' num2str(alpha1)]) 58 | disp(['alpha2: ' num2str(alpha2)]) 59 | 60 | % calculate model with updated parameters 61 | Ti = simulate(p0); 62 | Tp = simulate(p); 63 | 64 | % Plot results 65 | figure(1) 66 | 67 | subplot(3,1,1) 68 | plot(t/60.0,Ti(:,1),'y:','LineWidth',2) 69 | hold on 70 | plot(t/60.0,T1meas,'b-','LineWidth',2) 71 | plot(t/60.0,Tp(:,1),'r--','LineWidth',2) 72 | ylabel('Temperature (degC)') 73 | legend('T_1 initial','T_1 measured','T_1 optimized') 74 | 75 | subplot(3,1,2) 76 | plot(t/60.0,Ti(:,2),'y:','LineWidth',2) 77 | hold on 78 | plot(t/60.0,T2meas,'b-','LineWidth',2) 79 | plot(t/60.0,Tp(:,2),'r--','LineWidth',2) 80 | ylabel('Temperature (degC)') 81 | legend('T_2 initial','T_2 measured','T_2 optimized') 82 | 83 | subplot(3,1,3) 84 | plot(t/60.0,Q1,'g-','LineWidth',2) 85 | hold on 86 | plot(t/60.0,Q2,'k--','LineWidth',2) 87 | ylabel('Heater Output') 88 | legend('Q_1','Q_2') 89 | 90 | xlabel('Time (min)') --------------------------------------------------------------------------------